diff --git a/common/calculate-cpmm.ts b/common/calculate-cpmm.ts
index 493b5fa9..b5153355 100644
--- a/common/calculate-cpmm.ts
+++ b/common/calculate-cpmm.ts
@@ -123,6 +123,7 @@ export function calculateCpmmAmountToProb(
   prob: number,
   outcome: 'YES' | 'NO'
 ) {
+  if (prob <= 0 || prob >= 1 || isNaN(prob)) return Infinity
   if (outcome === 'NO') prob = 1 - prob
 
   // First, find an upper bound that leads to a more extreme probability than prob.
diff --git a/common/categories.ts b/common/categories.ts
index 232aa526..f302e3f2 100644
--- a/common/categories.ts
+++ b/common/categories.ts
@@ -1,6 +1,7 @@
 import { difference } from 'lodash'
 
 export const CATEGORIES_GROUP_SLUG_POSTFIX = '-default'
+
 export const CATEGORIES = {
   politics: 'Politics',
   technology: 'Technology',
@@ -30,10 +31,13 @@ export const EXCLUDED_CATEGORIES: category[] = [
   'manifold',
   'personal',
   'covid',
-  'culture',
   'gaming',
   'crypto',
-  'world',
 ]
 
 export const DEFAULT_CATEGORIES = difference(CATEGORY_LIST, EXCLUDED_CATEGORIES)
+
+export const DEFAULT_CATEGORY_GROUPS = DEFAULT_CATEGORIES.map((c) => ({
+  slug: c.toLowerCase() + CATEGORIES_GROUP_SLUG_POSTFIX,
+  name: CATEGORIES[c as category],
+}))
diff --git a/common/contract.ts b/common/contract.ts
index 5ddcf0b8..177af862 100644
--- a/common/contract.ts
+++ b/common/contract.ts
@@ -1,6 +1,7 @@
 import { Answer } from './answer'
 import { Fees } from './fees'
 import { JSONContent } from '@tiptap/core'
+import { GroupLink } from 'common/group'
 
 export type AnyMechanism = DPM | CPMM
 export type AnyOutcomeType = Binary | PseudoNumeric | FreeResponse | Numeric
@@ -46,8 +47,10 @@ export type Contract<T extends AnyContractType = AnyContractType> = {
   collectedFees: Fees
 
   groupSlugs?: string[]
+  groupLinks?: GroupLink[]
   uniqueBettorIds?: string[]
   uniqueBettorCount?: number
+  popularityScore?: number
 } & T
 
 export type BinaryContract = Contract & Binary
diff --git a/common/envs/prod.ts b/common/envs/prod.ts
index a5506b6e..2cad4203 100644
--- a/common/envs/prod.ts
+++ b/common/envs/prod.ts
@@ -23,6 +23,7 @@ export type EnvConfig = {
   // Currency controls
   fixedAnte?: number
   startingBalance?: number
+  referralBonus?: number
 }
 
 type FirebaseConfig = {
diff --git a/common/group.ts b/common/group.ts
index 15348d5a..7d3215ae 100644
--- a/common/group.ts
+++ b/common/group.ts
@@ -11,8 +11,19 @@ export type Group = {
   contractIds: string[]
 
   chatDisabled?: boolean
+  mostRecentChatActivityTime?: number
+  mostRecentContractAddedTime?: number
 }
 export const MAX_GROUP_NAME_LENGTH = 75
 export const MAX_ABOUT_LENGTH = 140
 export const MAX_ID_LENGTH = 60
 export const NEW_USER_GROUP_SLUGS = ['updates', 'bugs', 'welcome']
+export const GROUP_CHAT_SLUG = 'chat'
+
+export type GroupLink = {
+  slug: string
+  name: string
+  groupId: string
+  createdTime: number
+  userId?: string
+}
diff --git a/common/new-bet.ts b/common/new-bet.ts
index f484b9f7..1f5c0340 100644
--- a/common/new-bet.ts
+++ b/common/new-bet.ts
@@ -1,4 +1,4 @@
-import { sortBy, sumBy } from 'lodash'
+import { sortBy, sum, sumBy } from 'lodash'
 
 import { Bet, fill, LimitBet, MAX_LOAN_PER_CONTRACT, NumericBet } from './bet'
 import {
@@ -142,6 +142,13 @@ export const computeFills = (
   limitProb: number | undefined,
   unfilledBets: LimitBet[]
 ) => {
+  if (isNaN(betAmount)) {
+    throw new Error('Invalid bet amount: ${betAmount}')
+  }
+  if (isNaN(limitProb ?? 0)) {
+    throw new Error('Invalid limitProb: ${limitProb}')
+  }
+
   const sortedBets = sortBy(
     unfilledBets.filter((bet) => bet.outcome !== outcome),
     (bet) => (outcome === 'YES' ? bet.limitProb : -bet.limitProb),
@@ -239,6 +246,32 @@ export const getBinaryCpmmBetInfo = (
   }
 }
 
+export const getBinaryBetStats = (
+  outcome: 'YES' | 'NO',
+  betAmount: number,
+  contract: CPMMBinaryContract | PseudoNumericContract,
+  limitProb: number,
+  unfilledBets: LimitBet[]
+) => {
+  const { newBet } = getBinaryCpmmBetInfo(
+    outcome,
+    betAmount ?? 0,
+    contract,
+    limitProb,
+    unfilledBets as LimitBet[]
+  )
+  const remainingMatched =
+    ((newBet.orderAmount ?? 0) - newBet.amount) /
+    (outcome === 'YES' ? limitProb : 1 - limitProb)
+  const currentPayout = newBet.shares + remainingMatched
+
+  const currentReturn = betAmount ? (currentPayout - betAmount) / betAmount : 0
+
+  const totalFees = sum(Object.values(newBet.fees))
+
+  return { currentPayout, currentReturn, totalFees, newBet }
+}
+
 export const getNewBinaryDpmBetInfo = (
   outcome: 'YES' | 'NO',
   amount: number,
diff --git a/common/notification.ts b/common/notification.ts
index 63a44a52..5fd4236b 100644
--- a/common/notification.ts
+++ b/common/notification.ts
@@ -63,3 +63,4 @@ export type notification_reason_types =
   | 'on_group_you_are_member_of'
   | 'tip_received'
   | 'bet_fill'
+  | 'user_joined_from_your_group_invite'
diff --git a/common/numeric-constants.ts b/common/numeric-constants.ts
index 46885668..f399aa5a 100644
--- a/common/numeric-constants.ts
+++ b/common/numeric-constants.ts
@@ -3,4 +3,4 @@ export const NUMERIC_FIXED_VAR = 0.005
 
 export const NUMERIC_GRAPH_COLOR = '#5fa5f9'
 export const NUMERIC_TEXT_COLOR = 'text-blue-500'
-export const UNIQUE_BETTOR_BONUS_AMOUNT = 5
+export const UNIQUE_BETTOR_BONUS_AMOUNT = 10
diff --git a/common/pseudo-numeric.ts b/common/pseudo-numeric.ts
index c99e670f..ca62a80e 100644
--- a/common/pseudo-numeric.ts
+++ b/common/pseudo-numeric.ts
@@ -16,8 +16,8 @@ export const getMappedValue =
     const { min, max, isLogScale } = contract
 
     if (isLogScale) {
-      const logValue = p * Math.log10(max - min)
-      return 10 ** logValue + min
+      const logValue = p * Math.log10(max - min + 1)
+      return 10 ** logValue + min - 1
     }
 
     return p * (max - min) + min
@@ -37,8 +37,11 @@ export const getPseudoProbability = (
   max: number,
   isLogScale = false
 ) => {
+  if (value < min) return 0
+  if (value > max) return 1
+
   if (isLogScale) {
-    return Math.log10(value - min) / Math.log10(max - min)
+    return Math.log10(value - min + 1) / Math.log10(max - min + 1)
   }
 
   return (value - min) / (max - min)
diff --git a/common/user.ts b/common/user.ts
index 477139fd..0dac5a19 100644
--- a/common/user.ts
+++ b/common/user.ts
@@ -38,12 +38,14 @@ export type User = {
 
   referredByUserId?: string
   referredByContractId?: string
+  referredByGroupId?: string
+  lastPingTime?: number
 }
 
 export const STARTING_BALANCE = ENV_CONFIG.startingBalance ?? 1000
 // for sus users, i.e. multiple sign ups for same person
 export const SUS_STARTING_BALANCE = ENV_CONFIG.startingBalance ?? 10
-export const REFERRAL_AMOUNT = 500
+export const REFERRAL_AMOUNT = ENV_CONFIG.referralBonus ?? 500
 export type PrivateUser = {
   id: string // same as User.id
   username: string // denormalized from User
@@ -57,7 +59,6 @@ export type PrivateUser = {
   initialIpAddress?: string
   apiKey?: string
   notificationPreferences?: notification_subscribe_types
-  lastTimeCheckedBonuses?: number
 }
 
 export type notification_subscribe_types = 'all' | 'less' | 'none'
@@ -69,3 +70,6 @@ export type PortfolioMetrics = {
   timestamp: number
   userId: string
 }
+
+export const MANIFOLD_USERNAME = 'ManifoldMarkets'
+export const MANIFOLD_AVATAR_URL = 'https://manifold.markets/logo-bg-white.png'
diff --git a/common/util/format.ts b/common/util/format.ts
index decdd55d..4f123535 100644
--- a/common/util/format.ts
+++ b/common/util/format.ts
@@ -33,18 +33,24 @@ export function formatPercent(zeroToOne: number) {
   return (zeroToOne * 100).toFixed(decimalPlaces) + '%'
 }
 
+const showPrecision = (x: number, sigfigs: number) =>
+  // convert back to number for weird formatting reason
+  `${Number(x.toPrecision(sigfigs))}`
+
 // Eg 1234567.89 => 1.23M; 5678 => 5.68K
 export function formatLargeNumber(num: number, sigfigs = 2): string {
   const absNum = Math.abs(num)
-  if (absNum < 1000) {
-    return '' + Number(num.toPrecision(sigfigs))
-  }
+  if (absNum < 1) return showPrecision(num, sigfigs)
+
+  if (absNum < 100) return showPrecision(num, 2)
+  if (absNum < 1000) return showPrecision(num, 3)
+  if (absNum < 10000) return showPrecision(num, 4)
 
   const suffix = ['', 'K', 'M', 'B', 'T', 'Q']
-  const suffixIdx = Math.floor(Math.log10(absNum) / 3)
-  const suffixStr = suffix[suffixIdx]
-  const numStr = (num / Math.pow(10, 3 * suffixIdx)).toPrecision(sigfigs)
-  return `${Number(numStr)}${suffixStr}`
+  const i = Math.floor(Math.log10(absNum) / 3)
+
+  const numStr = showPrecision(num / Math.pow(10, 3 * i), sigfigs)
+  return `${numStr}${suffix[i] ?? ''}`
 }
 
 export function toCamelCase(words: string) {
diff --git a/common/util/parse.ts b/common/util/parse.ts
index 94b5ab7f..cdaa6a6c 100644
--- a/common/util/parse.ts
+++ b/common/util/parse.ts
@@ -20,6 +20,7 @@ import { Text } from '@tiptap/extension-text'
 // other tiptap extensions
 import { Image } from '@tiptap/extension-image'
 import { Link } from '@tiptap/extension-link'
+import Iframe from './tiptap-iframe'
 
 export function parseTags(text: string) {
   const regex = /(?:^|\s)(?:[#][a-z0-9_]+)/gi
@@ -49,6 +50,16 @@ export function parseWordsAsTags(text: string) {
   return parseTags(taggedText)
 }
 
+// TODO: fuzzy matching
+export const wordIn = (word: string, corpus: string) =>
+  corpus.toLocaleLowerCase().includes(word.toLocaleLowerCase())
+
+const checkAgainstQuery = (query: string, corpus: string) =>
+  query.split(' ').every((word) => wordIn(word, corpus))
+
+export const searchInAny = (query: string, ...fields: string[]) =>
+  fields.some((field) => checkAgainstQuery(query, field))
+
 // can't just do [StarterKit, Image...] because it doesn't work with cjs imports
 export const exhibitExts = [
   Blockquote,
@@ -70,6 +81,7 @@ export const exhibitExts = [
 
   Image,
   Link,
+  Iframe,
 ]
 // export const exhibitExts = [StarterKit as unknown as Extension, Image]
 
diff --git a/common/util/tiptap-iframe.ts b/common/util/tiptap-iframe.ts
new file mode 100644
index 00000000..5af63d2f
--- /dev/null
+++ b/common/util/tiptap-iframe.ts
@@ -0,0 +1,92 @@
+// Adopted from https://github.com/ueberdosis/tiptap/blob/main/demos/src/Experiments/Embeds/Vue/iframe.ts
+
+import { Node } from '@tiptap/core'
+
+export interface IframeOptions {
+  allowFullscreen: boolean
+  HTMLAttributes: {
+    [key: string]: any
+  }
+}
+
+declare module '@tiptap/core' {
+  interface Commands<ReturnType> {
+    iframe: {
+      setIframe: (options: { src: string }) => ReturnType
+    }
+  }
+}
+
+// These classes style the outer wrapper and the inner iframe;
+// Adopted from css in https://github.com/ueberdosis/tiptap/blob/main/demos/src/Experiments/Embeds/Vue/index.vue
+const wrapperClasses = 'relative h-auto w-full overflow-hidden'
+const iframeClasses = 'absolute top-0 left-0 h-full w-full'
+
+export default Node.create<IframeOptions>({
+  name: 'iframe',
+
+  group: 'block',
+
+  atom: true,
+
+  addOptions() {
+    return {
+      allowFullscreen: true,
+      HTMLAttributes: {
+        class: 'iframe-wrapper' + ' ' + wrapperClasses,
+        // Tailwind JIT doesn't seem to pick up `pb-[20rem]`, so we hack this in:
+        style: 'padding-bottom: 20rem;',
+      },
+    }
+  },
+
+  addAttributes() {
+    return {
+      src: {
+        default: null,
+      },
+      frameborder: {
+        default: 0,
+      },
+      allowfullscreen: {
+        default: this.options.allowFullscreen,
+        parseHTML: () => this.options.allowFullscreen,
+      },
+    }
+  },
+
+  parseHTML() {
+    return [{ tag: 'iframe' }]
+  },
+
+  renderHTML({ HTMLAttributes }) {
+    return [
+      'div',
+      this.options.HTMLAttributes,
+      [
+        'iframe',
+        {
+          ...HTMLAttributes,
+          class: HTMLAttributes.class + ' ' + iframeClasses,
+        },
+      ],
+    ]
+  },
+
+  addCommands() {
+    return {
+      setIframe:
+        (options: { src: string }) =>
+        ({ tr, dispatch }) => {
+          const { selection } = tr
+          const node = this.type.create(options)
+
+          if (dispatch) {
+            tr.replaceRangeWith(selection.from, selection.to, node)
+          }
+
+          return true
+        },
+    }
+  },
+})
diff --git a/docs/docs/api.md b/docs/docs/api.md
index 1cea6027..667c68b8 100644
--- a/docs/docs/api.md
+++ b/docs/docs/api.md
@@ -34,6 +34,18 @@ response was a 4xx or 5xx.)
 
 ## Endpoints
 
+### `GET /v0/user/[username]`
+
+Gets a user by their username. Remember that usernames may change.
+
+Requires no authorization.
+
+### `GET /v0/user/by-id/[id]`
+
+Gets a user by their unique ID. Many other API endpoints return this as the `userId`.
+
+Requires no authorization.
+
 ### `GET /v0/markets`
 
 Lists all markets, ordered by creation date descending.
@@ -627,6 +639,7 @@ Requires no authorization.
 
 ## Changelog
 
+- 2022-07-15: Add user by username and user by ID APIs
 - 2022-06-08: Add paging to markets endpoint
 - 2022-06-05: Add new authorized write endpoints
 - 2022-02-28: Add `resolutionTime` to markets, change `closeTime` definition
diff --git a/firestore.rules b/firestore.rules
index bd059f6a..0f28ca80 100644
--- a/firestore.rules
+++ b/firestore.rules
@@ -6,7 +6,12 @@ service cloud.firestore {
   match /databases/{database}/documents {
 
     function isAdmin() {
-      return request.auth.uid == 'tUosjZRN6GRv81uRksJ67EIF0853' // James
+      return request.auth.token.email in [
+        'akrolsmir@gmail.com',
+        'jahooma@gmail.com',
+        'taowell@gmail.com',
+        'manticmarkets@gmail.com'
+      ]
     }
 
     match /stats/stats {
@@ -17,16 +22,17 @@ service cloud.firestore {
       allow read;
       allow update: if resource.data.id == request.auth.uid
                        && request.resource.data.diff(resource.data).affectedKeys()
-                                                                    .hasOnly(['bio', 'bannerUrl', 'website', 'twitterHandle', 'discordHandle', 'followedCategories', 'referredByContractId']);
-          allow update: if resource.data.id == request.auth.uid
-                             && request.resource.data.diff(resource.data).affectedKeys()
-                              .hasOnly(['referredByUserId'])
-                              // only one referral allowed per user
-                              && !("referredByUserId" in resource.data)
-                              // user can't refer themselves
-                              && !(resource.data.id == request.resource.data.referredByUserId);
-                              // quid pro quos enabled (only once though so nbd) - bc I can't make this work:
-                              // && (get(/databases/$(database)/documents/users/$(request.resource.data.referredByUserId)).referredByUserId == resource.data.id);
+                                                                    .hasOnly(['bio', 'bannerUrl', 'website', 'twitterHandle', 'discordHandle', 'followedCategories', 'lastPingTime']);
+      // User referral rules
+      allow update: if resource.data.id == request.auth.uid
+                         && request.resource.data.diff(resource.data).affectedKeys()
+                          .hasOnly(['referredByUserId', 'referredByContractId', 'referredByGroupId'])
+                          // only one referral allowed per user
+                          && !("referredByUserId" in resource.data)
+                          // user can't refer themselves
+                          && !(resource.data.id == request.resource.data.referredByUserId);
+                          // quid pro quos enabled (only once though so nbd) - bc I can't make this work:
+                          // && (get(/databases/$(database)/documents/users/$(request.resource.data.referredByUserId)).referredByUserId == resource.data.id);
     }
 
   	match /{somePath=**}/portfolioHistory/{portfolioHistoryId} {
@@ -68,9 +74,9 @@ service cloud.firestore {
     match /contracts/{contractId} {
       allow read;
       allow update: if request.resource.data.diff(resource.data).affectedKeys()
-                                                                 .hasOnly(['tags', 'lowercaseTags', 'groupSlugs']);
+                                                                 .hasOnly(['tags', 'lowercaseTags', 'groupSlugs', 'groupLinks']);
       allow update: if request.resource.data.diff(resource.data).affectedKeys()
-                                                                 .hasOnly(['description', 'closeTime'])
+                                                                 .hasOnly(['description', 'closeTime', 'question'])
                        && resource.data.creatorId == request.auth.uid;
       allow update: if isAdmin();
           match /comments/{commentId} {
diff --git a/functions/README.md b/functions/README.md
index 8013fb20..97a7a33b 100644
--- a/functions/README.md
+++ b/functions/README.md
@@ -27,6 +27,7 @@ Adapted from https://firebase.google.com/docs/functions/get-started
 
    1. `$ brew install java`
    2. `$ sudo ln -sfn /opt/homebrew/opt/openjdk/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk.jdk`
+
 2. `$ gcloud auth login` to authenticate the CLI tools to Google Cloud
 3. `$ gcloud config set project <project-id>` to choose the project (`$ gcloud projects list` to see options)
 4. `$ mkdir firestore_export` to create a folder to store the exported database
@@ -53,7 +54,10 @@ Adapted from https://firebase.google.com/docs/functions/get-started
 
 ## Deploying
 
-0. `$ firebase use prod` to switch to prod
+0. After merging, you need to manually deploy to backend:
+1. `git checkout main`
+1. `git pull origin main`
+1. `$ firebase use prod` to switch to prod
 1. `$ firebase deploy --only functions` to push your changes live!
    (Future TODO: auto-deploy functions on Git push)
 
diff --git a/functions/src/claim-manalink.ts b/functions/src/claim-manalink.ts
index 3822bbf7..b534f0a3 100644
--- a/functions/src/claim-manalink.ts
+++ b/functions/src/claim-manalink.ts
@@ -28,6 +28,9 @@ export const claimmanalink = newEndpoint({}, async (req, auth) => {
     if (amount <= 0 || isNaN(amount) || !isFinite(amount))
       throw new APIError(500, 'Invalid amount')
 
+    if (auth.uid === fromId)
+      throw new APIError(400, `You can't claim your own manalink`)
+
     const fromDoc = firestore.doc(`users/${fromId}`)
     const fromSnap = await transaction.get(fromDoc)
     if (!fromSnap.exists) {
diff --git a/functions/src/create-notification.ts b/functions/src/create-notification.ts
index 1fb6c3af..7cc05760 100644
--- a/functions/src/create-notification.ts
+++ b/functions/src/create-notification.ts
@@ -15,11 +15,11 @@ import { Answer } from '../../common/answer'
 import { getContractBetMetrics } from '../../common/calculate'
 import { removeUndefinedProps } from '../../common/util/object'
 import { TipTxn } from '../../common/txn'
-import { Group } from '../../common/group'
+import { Group, GROUP_CHAT_SLUG } from '../../common/group'
 const firestore = admin.firestore()
 
 type user_to_reason_texts = {
-  [userId: string]: { reason: notification_reason_types; isSeeOnHref?: string }
+  [userId: string]: { reason: notification_reason_types }
 }
 
 export const createNotification = async (
@@ -29,12 +29,22 @@ export const createNotification = async (
   sourceUser: User,
   idempotencyKey: string,
   sourceText: string,
-  sourceContract?: Contract,
-  relatedSourceType?: notification_source_types,
-  relatedUserId?: string,
-  sourceSlug?: string,
-  sourceTitle?: string
+  miscData?: {
+    contract?: Contract
+    relatedSourceType?: notification_source_types
+    relatedUserId?: string
+    slug?: string
+    title?: string
+  }
 ) => {
+  const {
+    contract: sourceContract,
+    relatedSourceType,
+    relatedUserId,
+    slug,
+    title,
+  } = miscData ?? {}
+
   const shouldGetNotification = (
     userId: string,
     userToReasonTexts: user_to_reason_texts
@@ -70,9 +80,8 @@ export const createNotification = async (
           sourceContractCreatorUsername: sourceContract?.creatorUsername,
           sourceContractTitle: sourceContract?.question,
           sourceContractSlug: sourceContract?.slug,
-          sourceSlug: sourceSlug ? sourceSlug : sourceContract?.slug,
-          sourceTitle: sourceTitle ? sourceTitle : sourceContract?.question,
-          isSeenOnHref: userToReasonTexts[userId].isSeeOnHref,
+          sourceSlug: slug ? slug : sourceContract?.slug,
+          sourceTitle: title ? title : sourceContract?.question,
         }
         await notificationRef.set(removeUndefinedProps(notification))
       })
@@ -254,20 +263,6 @@ export const createNotification = async (
       }
   }
 
-  const notifyUserReceivedReferralBonus = async (
-    userToReasonTexts: user_to_reason_texts,
-    relatedUserId: string
-  ) => {
-    if (shouldGetNotification(relatedUserId, userToReasonTexts))
-      userToReasonTexts[relatedUserId] = {
-        // If the referrer is the market creator, just tell them they joined to bet on their market
-        reason:
-          sourceContract?.creatorId === relatedUserId
-            ? 'user_joined_to_bet_on_your_market'
-            : 'you_referred_user',
-      }
-  }
-
   const notifyContractCreatorOfUniqueBettorsBonus = async (
     userToReasonTexts: user_to_reason_texts,
     userId: string
@@ -277,17 +272,6 @@ export const createNotification = async (
     }
   }
 
-  const notifyOtherGroupMembersOfComment = async (
-    userToReasons: user_to_reason_texts,
-    userId: string
-  ) => {
-    if (shouldGetNotification(userId, userToReasons))
-      userToReasons[userId] = {
-        reason: 'on_group_you_are_member_of',
-        isSeeOnHref: sourceSlug,
-      }
-  }
-
   const getUsersToNotify = async () => {
     const userToReasonTexts: user_to_reason_texts = {}
     // The following functions modify the userToReasonTexts object in place.
@@ -296,10 +280,6 @@ export const createNotification = async (
     } else if (sourceType === 'group' && relatedUserId) {
       if (sourceUpdateType === 'created')
         await notifyUserAddedToGroup(userToReasonTexts, relatedUserId)
-    } else if (sourceType === 'user' && relatedUserId) {
-      await notifyUserReceivedReferralBonus(userToReasonTexts, relatedUserId)
-    } else if (sourceType === 'comment' && !sourceContract && relatedUserId) {
-      await notifyOtherGroupMembersOfComment(userToReasonTexts, relatedUserId)
     }
 
     // The following functions need sourceContract to be defined.
@@ -417,3 +397,84 @@ export const createBetFillNotification = async (
   }
   return await notificationRef.set(removeUndefinedProps(notification))
 }
+
+export const createGroupCommentNotification = async (
+  fromUser: User,
+  toUserId: string,
+  comment: Comment,
+  group: Group,
+  idempotencyKey: string
+) => {
+  if (toUserId === fromUser.id) return
+  const notificationRef = firestore
+    .collection(`/users/${toUserId}/notifications`)
+    .doc(idempotencyKey)
+  const sourceSlug = `/group/${group.slug}/${GROUP_CHAT_SLUG}`
+  const notification: Notification = {
+    id: idempotencyKey,
+    userId: toUserId,
+    reason: 'on_group_you_are_member_of',
+    createdTime: Date.now(),
+    isSeen: false,
+    sourceId: comment.id,
+    sourceType: 'comment',
+    sourceUpdateType: 'created',
+    sourceUserName: fromUser.name,
+    sourceUserUsername: fromUser.username,
+    sourceUserAvatarUrl: fromUser.avatarUrl,
+    sourceText: comment.text,
+    sourceSlug,
+    sourceTitle: `${group.name}`,
+    isSeenOnHref: sourceSlug,
+  }
+  await notificationRef.set(removeUndefinedProps(notification))
+}
+
+export const createReferralNotification = async (
+  toUser: User,
+  referredUser: User,
+  idempotencyKey: string,
+  bonusAmount: string,
+  referredByContract?: Contract,
+  referredByGroup?: Group
+) => {
+  const notificationRef = firestore
+    .collection(`/users/${toUser.id}/notifications`)
+    .doc(idempotencyKey)
+  const notification: Notification = {
+    id: idempotencyKey,
+    userId: toUser.id,
+    reason: referredByGroup
+      ? 'user_joined_from_your_group_invite'
+      : referredByContract?.creatorId === toUser.id
+      ? 'user_joined_to_bet_on_your_market'
+      : 'you_referred_user',
+    createdTime: Date.now(),
+    isSeen: false,
+    sourceId: referredUser.id,
+    sourceType: 'user',
+    sourceUpdateType: 'updated',
+    sourceContractId: referredByContract?.id,
+    sourceUserName: referredUser.name,
+    sourceUserUsername: referredUser.username,
+    sourceUserAvatarUrl: referredUser.avatarUrl,
+    sourceText: bonusAmount,
+    // Only pass the contract referral details if they weren't referred to a group
+    sourceContractCreatorUsername: !referredByGroup
+      ? referredByContract?.creatorUsername
+      : undefined,
+    sourceContractTitle: !referredByGroup
+      ? referredByContract?.question
+      : undefined,
+    sourceContractSlug: !referredByGroup ? referredByContract?.slug : undefined,
+    sourceSlug: referredByGroup
+      ? groupPath(referredByGroup.slug)
+      : referredByContract?.slug,
+    sourceTitle: referredByGroup
+      ? referredByGroup.name
+      : referredByContract?.question,
+  }
+  await notificationRef.set(removeUndefinedProps(notification))
+}
+
+const groupPath = (groupSlug: string) => `/group/${groupSlug}`
diff --git a/functions/src/create-user.ts b/functions/src/create-user.ts
index 332c1872..1f413b6d 100644
--- a/functions/src/create-user.ts
+++ b/functions/src/create-user.ts
@@ -1,6 +1,8 @@
 import * as admin from 'firebase-admin'
 import { z } from 'zod'
 import {
+  MANIFOLD_AVATAR_URL,
+  MANIFOLD_USERNAME,
   PrivateUser,
   STARTING_BALANCE,
   SUS_STARTING_BALANCE,
@@ -157,11 +159,11 @@ const addUserToDefaultGroups = async (user: User) => {
         id: welcomeCommentDoc.id,
         groupId: group.id,
         userId: manifoldAccount,
-        text: `Welcome, ${user.name} (@${user.username})!`,
+        text: `Welcome, @${user.username} aka ${user.name}!`,
         createdTime: Date.now(),
         userName: 'Manifold Markets',
-        userUsername: 'ManifoldMarkets',
-        userAvatarUrl: 'https://manifold.markets/logo-bg-white.png',
+        userUsername: MANIFOLD_USERNAME,
+        userAvatarUrl: MANIFOLD_AVATAR_URL,
       })
     }
   }
diff --git a/functions/src/email-templates/500-mana.html b/functions/src/email-templates/500-mana.html
new file mode 100644
index 00000000..5f0c450e
--- /dev/null
+++ b/functions/src/email-templates/500-mana.html
@@ -0,0 +1,29 @@
+<iframe
+  style="border: 0px; width: 100%; height: 100%"
+  seamless
+  sandbox
+  srcdoc='<!doctype html><html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office"><head><title>7th Day Anniversary Gift!</title><!--[if !mso]><!--><meta http-equiv="X-UA-Compatible" content="IE=edge"><!--<![endif]--><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style type="text/css">#outlook a { padding:0; }
+          body { margin:0;padding:0;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%; }
+          table, td { border-collapse:collapse;mso-table-lspace:0pt;mso-table-rspace:0pt; }
+          img { border:0;height:auto;line-height:100%; outline:none;text-decoration:none;-ms-interpolation-mode:bicubic; }
+          p { display:block;margin:13px 0; }</style><!--[if mso]>
+        <noscript>
+        <xml>
+        <o:OfficeDocumentSettings>
+          <o:AllowPNG/>
+          <o:PixelsPerInch>96</o:PixelsPerInch>
+        </o:OfficeDocumentSettings>
+        </xml>
+        </noscript>
+        <![endif]--><!--[if lte mso 11]>
+        <style type="text/css">
+          .mj-outlook-group-fix { width:100% !important; }
+        </style>
+        <![endif]--><style type="text/css">@media only screen and (min-width:480px) {
+        .mj-column-per-100 { width:100% !important; max-width: 100%; }
+      }</style><style media="screen and (min-width:480px)">.moz-text-html .mj-column-per-100 { width:100% !important; max-width: 100%; }</style><style type="text/css">[owa] .mj-column-per-100 { width:100% !important; max-width: 100%; }</style><style type="text/css">@media only screen and (max-width:480px) {
+      table.mj-full-width-mobile { width: 100% !important; }
+      td.mj-full-width-mobile { width: auto !important; }
+    }</style></head><body style="word-spacing:normal;background-color:#F4F4F4;"><div style="background-color:#F4F4F4;"><!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]--><div style="background:#ffffff;background-color:#ffffff;margin:0px auto;max-width:600px;"><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#ffffff;background-color:#ffffff;width:100%;"><tbody><tr><td style="direction:ltr;font-size:0px;padding:0px 0px 0px 0px;padding-bottom:0px;padding-left:0px;padding-right:0px;padding-top:0px;text-align:center;"><!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]--><div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%"><tbody><tr><td align="center" style="font-size:0px;padding:0px 25px 0px 25px;padding-top:0px;padding-right:25px;padding-bottom:0px;padding-left:25px;word-break:break-word;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;"><tbody><tr><td style="width:550px;"><a href="https://manifold.markets/home" target="_blank"><img alt="" height="auto" src="https://03jlj.mjt.lu/img/03jlj/b/u71/sjvu.gif" style="border:none;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;" width="550"></a></td></tr></tbody></table></td></tr><tr><td align="left" style="font-size:0px;padding:10px 25px;padding-top:0px;padding-bottom:0px;word-break:break-word;"><div style="font-family:Arial, sans-serif;font-size:18px;letter-spacing:normal;line-height:1;text-align:left;color:#000000;"><p class="text-build-content" style="text-align: center; margin: 10px 0; margin-top: 10px; margin-bottom: 10px;" data-testid="4XoHRGw1Y"><span style="color:#000000;font-family:Arial, Helvetica, sans-serif;font-size:18px;">Running low on Mana? Click the link below to recieve a one time gift of 500 Mana!</span></p></div></td></tr><tr><td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;"><tbody><tr><td style="width:550px;"><a href="https://manifold.markets/link/lj4JbBvE" target="_blank"><img alt="" height="auto" src="https://03jlj.mjt.lu/img/03jlj/b/u71/4pg3.png" style="border:none;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;" width="550"></a></td></tr></tbody></table></td></tr><tr><td align="left" style="font-size:0px;padding:15px 25px 0px 25px;padding-top:15px;padding-right:25px;padding-bottom:0px;padding-left:25px;word-break:break-word;"><div style="font-family:Arial, sans-serif;font-size:18px;letter-spacing:normal;line-height:1;text-align:left;color:#000000;"><p class="text-build-content" style="line-height: 23px; margin: 10px 0; margin-top: 10px;" data-testid="3Q8BP69fq"><span style="font-family:Arial, sans-serif;font-size:18px;">Did you know, besides making correct predictions, there are plenty of other ways to earn Mana?</span></p><ul><li style="line-height:23px;"><span style="font-family:Arial, sans-serif;font-size:18px;">Recieving tips on comments</span></li><li style="line-height:23px;"><span style="font-family:Arial, sans-serif;font-size:18px;">Unique trader bonus for each user who bets on you markets</span></li><li style="line-height:23px;"><span style="font-family:Arial, sans-serif;font-size:18px;">Reffering friends (click the share button on &nbsp;a market or group!)</span></li><li style="line-height:23px;"><a class="link-build-content" style="color:inherit;; text-decoration: none;" target="_blank" href="https://manifold.markets/group/bugs?s=most-traded"><span style="color:#55575d;font-family:Arial;font-size:18px;"><u>Reporting bugs</u></span></a><span style="font-family:Arial, sans-serif;font-size:18px;"> and </span><a class="link-build-content" style="color:inherit;; text-decoration: none;" target="_blank" href="https://manifold.markets/group/manifold-features-25bad7c7792e/chat?s=most-traded"><span style="color:#55575d;font-family:Arial;font-size:18px;"><u>giving feedback</u></span></a></li></ul><p class="text-build-content" data-testid="3Q8BP69fq" style="margin: 10px 0;">&nbsp;</p><p class="text-build-content" data-testid="3Q8BP69fq" style="margin: 10px 0;"><span style="color:#000000;font-family:Arial;font-size:18px;">Cheers,</span></p><p class="text-build-content" data-testid="3Q8BP69fq" style="margin: 10px 0;"><span style="color:#000000;font-family:Arial;font-size:18px;">David from Manifold</span></p><p class="text-build-content" data-testid="3Q8BP69fq" style="margin: 10px 0; margin-bottom: 10px;">&nbsp;</p></div></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></table><![endif]--></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]--><div style="margin:0px auto;max-width:600px;"><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;"><tbody><tr><td style="direction:ltr;font-size:0px;padding:0 0 20px 0;text-align:center;"><!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]--><div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%"><tbody><tr><td align="center" style="font-size:0px;padding:0px;word-break:break-word;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
+       </div><!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]--><div style="margin:0px auto;max-width:600px;"><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;"><tbody><tr><td style="direction:ltr;font-size:0px;padding:20px 0px 20px 0px;text-align:center;"><!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]--><div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%"><tbody><tr><td style="vertical-align:top;padding:0;"><table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%"><tbody><tr><td align="center" style="font-size:0px;padding:10px 25px;padding-top:0px;padding-bottom:0px;word-break:break-word;"><div style="font-family:Arial, sans-serif;font-size:11px;letter-spacing:normal;line-height:22px;text-align:center;color:#000000;"><p style="margin: 10px 0;">This e-mail has been sent to [[EMAIL_TO]], <a href="{{unsubscribeLink}}” style="color:inherit;text-decoration:none;" target="_blank">click here to unsubscribe</a>.</p></div></td></tr><tr><td align="center" style="font-size:0px;padding:10px 25px;padding-top:0px;padding-bottom:0px;word-break:break-word;"><div style="font-family:Arial, sans-serif;font-size:11px;letter-spacing:normal;line-height:22px;text-align:center;color:#000000;"></div></td></tr></tbody></table></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></table><![endif]--></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></table><![endif]--></div></body></html>'
+></iframe>
diff --git a/functions/src/emails.ts b/functions/src/emails.ts
index 60534679..a29f982c 100644
--- a/functions/src/emails.ts
+++ b/functions/src/emails.ts
@@ -302,7 +302,7 @@ export const sendNewCommentEmail = async (
     )}`
   }
 
-  const subject = `Comment from ${commentorName} on ${question}`
+  const subject = `Comment on ${question}`
   const from = `${commentorName} on Manifold <no-reply@manifold.markets>`
 
   if (contract.outcomeType === 'FREE_RESPONSE' && answerId && answerText) {
diff --git a/functions/src/index.ts b/functions/src/index.ts
index 3055f8dc..df311886 100644
--- a/functions/src/index.ts
+++ b/functions/src/index.ts
@@ -22,6 +22,7 @@ export * from './on-update-user'
 export * from './on-create-comment-on-group'
 export * from './on-create-txn'
 export * from './on-delete-group'
+export * from './score-contracts'
 
 // v2
 export * from './health'
diff --git a/functions/src/market-close-notifications.ts b/functions/src/market-close-notifications.ts
index ee9952bf..f31674a1 100644
--- a/functions/src/market-close-notifications.ts
+++ b/functions/src/market-close-notifications.ts
@@ -64,7 +64,7 @@ async function sendMarketCloseEmails() {
       user,
       'closed' + contract.id.slice(6, contract.id.length),
       contract.closeTime?.toString() ?? new Date().toString(),
-      contract
+      { contract }
     )
   }
 }
diff --git a/functions/src/on-create-answer.ts b/functions/src/on-create-answer.ts
index 78fd1399..af4690b0 100644
--- a/functions/src/on-create-answer.ts
+++ b/functions/src/on-create-answer.ts
@@ -28,6 +28,6 @@ export const onCreateAnswer = functions.firestore
       answerCreator,
       eventId,
       answer.text,
-      contract
+      { contract }
     )
   })
diff --git a/functions/src/on-create-bet.ts b/functions/src/on-create-bet.ts
index fc2e0053..d33e71dd 100644
--- a/functions/src/on-create-bet.ts
+++ b/functions/src/on-create-bet.ts
@@ -64,10 +64,7 @@ const updateUniqueBettorsAndGiveCreatorBonus = async (
 
   if (!previousUniqueBettorIds) {
     const contractBets = (
-      await firestore
-        .collection(`contracts/${contractId}/bets`)
-        .where('userId', '!=', contract.creatorId)
-        .get()
+      await firestore.collection(`contracts/${contractId}/bets`).get()
     ).docs.map((doc) => doc.data() as Bet)
 
     if (contractBets.length === 0) {
@@ -82,9 +79,7 @@ const updateUniqueBettorsAndGiveCreatorBonus = async (
     )
   }
 
-  const isNewUniqueBettor =
-    !previousUniqueBettorIds.includes(bettorId) &&
-    bettorId !== contract.creatorId
+  const isNewUniqueBettor = !previousUniqueBettorIds.includes(bettorId)
 
   const newUniqueBettorIds = uniq([...previousUniqueBettorIds, bettorId])
   // Update contract unique bettors
@@ -96,7 +91,9 @@ const updateUniqueBettorsAndGiveCreatorBonus = async (
       uniqueBettorCount: newUniqueBettorIds.length,
     })
   }
-  if (!isNewUniqueBettor) return
+
+  // No need to give a bonus for the creator's bet
+  if (!isNewUniqueBettor || bettorId == contract.creatorId) return
 
   // Create combined txn for all new unique bettors
   const bonusTxnDetails = {
@@ -134,12 +131,11 @@ const updateUniqueBettorsAndGiveCreatorBonus = async (
       fromUser,
       eventId + '-bonus',
       result.txn.amount + '',
-      contract,
-      undefined,
-      // No need to set the user id, we'll use the contract creator id
-      undefined,
-      contract.slug,
-      contract.question
+      {
+        contract,
+        slug: contract.slug,
+        title: contract.question,
+      }
     )
   }
 }
diff --git a/functions/src/on-create-comment-on-contract.ts b/functions/src/on-create-comment-on-contract.ts
index f7839b44..8d841ac0 100644
--- a/functions/src/on-create-comment-on-contract.ts
+++ b/functions/src/on-create-comment-on-contract.ts
@@ -68,7 +68,7 @@ export const onCreateCommentOnContract = functions
       ? 'answer'
       : undefined
 
-    const relatedUser = comment.replyToCommentId
+    const relatedUserId = comment.replyToCommentId
       ? comments.find((c) => c.id === comment.replyToCommentId)?.userId
       : answer?.userId
 
@@ -79,9 +79,7 @@ export const onCreateCommentOnContract = functions
       commentCreator,
       eventId,
       comment.text,
-      contract,
-      relatedSourceType,
-      relatedUser
+      { contract, relatedSourceType, relatedUserId }
     )
 
     const recipientUserIds = uniq([
diff --git a/functions/src/on-create-comment-on-group.ts b/functions/src/on-create-comment-on-group.ts
index 7217e602..0064480f 100644
--- a/functions/src/on-create-comment-on-group.ts
+++ b/functions/src/on-create-comment-on-group.ts
@@ -3,7 +3,7 @@ import { Comment } from '../../common/comment'
 import * as admin from 'firebase-admin'
 import { Group } from '../../common/group'
 import { User } from '../../common/user'
-import { createNotification } from './create-notification'
+import { createGroupCommentNotification } from './create-notification'
 const firestore = admin.firestore()
 
 export const onCreateCommentOnGroup = functions.firestore
@@ -29,23 +29,17 @@ export const onCreateCommentOnGroup = functions.firestore
 
     const group = groupSnapshot.data() as Group
     await firestore.collection('groups').doc(groupId).update({
-      mostRecentActivityTime: comment.createdTime,
+      mostRecentChatActivityTime: comment.createdTime,
     })
 
     await Promise.all(
       group.memberIds.map(async (memberId) => {
-        return await createNotification(
-          comment.id,
-          'comment',
-          'created',
+        return await createGroupCommentNotification(
           creatorSnapshot.data() as User,
-          eventId,
-          comment.text,
-          undefined,
-          undefined,
           memberId,
-          `/group/${group.slug}`,
-          `${group.name}`
+          comment,
+          group,
+          eventId
         )
       })
     )
diff --git a/functions/src/on-create-contract.ts b/functions/src/on-create-contract.ts
index 28682793..a43beda7 100644
--- a/functions/src/on-create-contract.ts
+++ b/functions/src/on-create-contract.ts
@@ -21,6 +21,6 @@ export const onCreateContract = functions.firestore
       contractCreator,
       eventId,
       richTextToString(contract.description as JSONContent),
-      contract
+      { contract }
     )
   })
diff --git a/functions/src/on-create-group.ts b/functions/src/on-create-group.ts
index 1d041c04..47618d7a 100644
--- a/functions/src/on-create-group.ts
+++ b/functions/src/on-create-group.ts
@@ -20,11 +20,11 @@ export const onCreateGroup = functions.firestore
         groupCreator,
         eventId,
         group.about,
-        undefined,
-        undefined,
-        memberId,
-        group.slug,
-        group.name
+        {
+          relatedUserId: memberId,
+          slug: group.slug,
+          title: group.name,
+        }
       )
     }
   })
diff --git a/functions/src/on-create-liquidity-provision.ts b/functions/src/on-create-liquidity-provision.ts
index d55b2be4..ba17f3e7 100644
--- a/functions/src/on-create-liquidity-provision.ts
+++ b/functions/src/on-create-liquidity-provision.ts
@@ -26,6 +26,6 @@ export const onCreateLiquidityProvision = functions.firestore
       liquidityProvider,
       eventId,
       liquidity.amount.toString(),
-      contract
+      { contract }
     )
   })
diff --git a/functions/src/on-delete-group.ts b/functions/src/on-delete-group.ts
index ca833254..e5531d7b 100644
--- a/functions/src/on-delete-group.ts
+++ b/functions/src/on-delete-group.ts
@@ -3,6 +3,7 @@ import * as admin from 'firebase-admin'
 
 import { Group } from 'common/group'
 import { Contract } from 'common/contract'
+
 const firestore = admin.firestore()
 
 export const onDeleteGroup = functions.firestore
@@ -15,17 +16,21 @@ export const onDeleteGroup = functions.firestore
       .collection('contracts')
       .where('groupSlugs', 'array-contains', group.slug)
       .get()
+    console.log("contracts with group's slug:", contracts)
 
     for (const doc of contracts.docs) {
       const contract = doc.data() as Contract
+      const newGroupLinks = contract.groupLinks?.filter(
+        (link) => link.slug !== group.slug
+      )
+
       // remove the group from the contract
       await firestore
         .collection('contracts')
         .doc(contract.id)
         .update({
-          groupSlugs: (contract.groupSlugs ?? []).filter(
-            (groupSlug) => groupSlug !== group.slug
-          ),
+          groupSlugs: contract.groupSlugs?.filter((s) => s !== group.slug),
+          groupLinks: newGroupLinks ?? [],
         })
     }
   })
diff --git a/functions/src/on-follow-user.ts b/functions/src/on-follow-user.ts
index ad85f4d3..9a6e6dce 100644
--- a/functions/src/on-follow-user.ts
+++ b/functions/src/on-follow-user.ts
@@ -30,9 +30,7 @@ export const onFollowUser = functions.firestore
       followingUser,
       eventId,
       '',
-      undefined,
-      undefined,
-      follow.userId
+      { relatedUserId: follow.userId }
     )
   })
 
diff --git a/functions/src/on-update-contract.ts b/functions/src/on-update-contract.ts
index 4674bd82..2042f726 100644
--- a/functions/src/on-update-contract.ts
+++ b/functions/src/on-update-contract.ts
@@ -36,7 +36,7 @@ export const onUpdateContract = functions.firestore
         contractUpdater,
         eventId,
         resolutionText,
-        contract
+        { contract }
       )
     } else if (
       previousValue.closeTime !== contract.closeTime ||
@@ -62,7 +62,7 @@ export const onUpdateContract = functions.firestore
         contractUpdater,
         eventId,
         sourceText,
-        contract
+        { contract }
       )
     }
   })
diff --git a/functions/src/on-update-group.ts b/functions/src/on-update-group.ts
index feaa6443..3ab2a249 100644
--- a/functions/src/on-update-group.ts
+++ b/functions/src/on-update-group.ts
@@ -12,7 +12,15 @@ export const onUpdateGroup = functions.firestore
     // ignore the update we just made
     if (prevGroup.mostRecentActivityTime !== group.mostRecentActivityTime)
       return
-    // TODO: create notification with isSeeOnHref set to the group's /group/questions url
+
+    if (prevGroup.contractIds.length < group.contractIds.length) {
+      await firestore
+        .collection('groups')
+        .doc(group.id)
+        .update({ mostRecentContractAddedTime: Date.now() })
+      //TODO: create notification with isSeeOnHref set to the group's /group/slug/questions url
+      // but first, let the new /group/slug/chat notification permeate so that we can differentiate between the two
+    }
 
     await firestore
       .collection('groups')
diff --git a/functions/src/on-update-user.ts b/functions/src/on-update-user.ts
index 0ace3c53..f5558730 100644
--- a/functions/src/on-update-user.ts
+++ b/functions/src/on-update-user.ts
@@ -2,11 +2,12 @@ import * as functions from 'firebase-functions'
 import * as admin from 'firebase-admin'
 import { REFERRAL_AMOUNT, User } from '../../common/user'
 import { HOUSE_LIQUIDITY_PROVIDER_ID } from '../../common/antes'
-import { createNotification } from './create-notification'
+import { createReferralNotification } from './create-notification'
 import { ReferralTxn } from '../../common/txn'
 import { Contract } from '../../common/contract'
 import { LimitBet } from 'common/bet'
 import { QuerySnapshot } from 'firebase-admin/firestore'
+import { Group } from 'common/group'
 const firestore = admin.firestore()
 
 export const onUpdateUser = functions.firestore
@@ -54,6 +55,17 @@ async function handleUserUpdatedReferral(user: User, eventId: string) {
     }
     console.log(`referredByContract: ${referredByContract}`)
 
+    let referredByGroup: Group | undefined = undefined
+    if (user.referredByGroupId) {
+      const referredByGroupDoc = firestore.doc(
+        `groups/${user.referredByGroupId}`
+      )
+      referredByGroup = await transaction
+        .get(referredByGroupDoc)
+        .then((snap) => snap.data() as Group)
+    }
+    console.log(`referredByGroup: ${referredByGroup}`)
+
     const txns = (
       await firestore
         .collection('txns')
@@ -100,18 +112,13 @@ async function handleUserUpdatedReferral(user: User, eventId: string) {
       totalDeposits: referredByUser.totalDeposits + REFERRAL_AMOUNT,
     })
 
-    await createNotification(
-      user.id,
-      'user',
-      'updated',
+    await createReferralNotification(
+      referredByUser,
       user,
       eventId,
       txn.amount.toString(),
       referredByContract,
-      'user',
-      referredByUser.id,
-      referredByContract?.slug,
-      referredByContract?.question
+      referredByGroup
     )
   })
 }
diff --git a/functions/src/place-bet.ts b/functions/src/place-bet.ts
index b8c0ca0e..97ff9780 100644
--- a/functions/src/place-bet.ts
+++ b/functions/src/place-bet.ts
@@ -6,7 +6,7 @@ import {
   Query,
   Transaction,
 } from 'firebase-admin/firestore'
-import { groupBy, mapValues, sumBy } from 'lodash'
+import { groupBy, mapValues, sumBy, uniq } from 'lodash'
 
 import { APIError, newEndpoint, validate } from './api'
 import { Contract, CPMM_MIN_POOL_QTY } from '../../common/contract'
@@ -153,10 +153,10 @@ export const placebet = newEndpoint({}, async (req, auth) => {
   log('Main transaction finished.')
 
   if (result.newBet.amount !== 0) {
-    const userIds = [
+    const userIds = uniq([
       auth.uid,
       ...(result.makers ?? []).map((maker) => maker.bet.userId),
-    ]
+    ])
     await Promise.all(userIds.map((userId) => redeemShares(userId, contractId)))
     log('Share redemption transaction finished.')
   }
diff --git a/functions/src/score-contracts.ts b/functions/src/score-contracts.ts
new file mode 100644
index 00000000..57976ff2
--- /dev/null
+++ b/functions/src/score-contracts.ts
@@ -0,0 +1,54 @@
+import * as functions from 'firebase-functions'
+import * as admin from 'firebase-admin'
+import { Bet } from 'common/bet'
+import { uniq } from 'lodash'
+import { Contract } from 'common/contract'
+import { log } from './utils'
+
+export const scoreContracts = functions.pubsub
+  .schedule('every 1 hours')
+  .onRun(async () => {
+    await scoreContractsInternal()
+  })
+const firestore = admin.firestore()
+
+async function scoreContractsInternal() {
+  const now = Date.now()
+  const lastHour = now - 60 * 60 * 1000
+  const last3Days = now - 1000 * 60 * 60 * 24 * 3
+  const activeContractsSnap = await firestore
+    .collection('contracts')
+    .where('lastUpdatedTime', '>', lastHour)
+    .get()
+  const activeContracts = activeContractsSnap.docs.map(
+    (doc) => doc.data() as Contract
+  )
+  // We have to downgrade previously active contracts to allow the new ones to bubble up
+  const previouslyActiveContractsSnap = await firestore
+    .collection('contracts')
+    .where('popularityScore', '>', 0)
+    .get()
+  const activeContractIds = activeContracts.map((c) => c.id)
+  const previouslyActiveContracts = previouslyActiveContractsSnap.docs
+    .map((doc) => doc.data() as Contract)
+    .filter((c) => !activeContractIds.includes(c.id))
+
+  const contracts = activeContracts.concat(previouslyActiveContracts)
+  log(`Found ${contracts.length} contracts to score`)
+
+  for (const contract of contracts) {
+    const bets = await firestore
+      .collection(`contracts/${contract.id}/bets`)
+      .where('createdTime', '>', last3Days)
+      .get()
+    const bettors = bets.docs
+      .map((doc) => doc.data() as Bet)
+      .map((bet) => bet.userId)
+    const score = uniq(bettors).length
+    if (contract.popularityScore !== score)
+      await firestore
+        .collection('contracts')
+        .doc(contract.id)
+        .update({ popularityScore: score })
+  }
+}
diff --git a/functions/src/scripts/convert-categories.ts b/functions/src/scripts/convert-categories.ts
index 8fe90807..3436bcbc 100644
--- a/functions/src/scripts/convert-categories.ts
+++ b/functions/src/scripts/convert-categories.ts
@@ -1,14 +1,9 @@
 import * as admin from 'firebase-admin'
 
 import { initAdmin } from './script-init'
-initAdmin()
-
 import { getValues, isProd } from '../utils'
-import {
-  CATEGORIES_GROUP_SLUG_POSTFIX,
-  DEFAULT_CATEGORIES,
-} from 'common/categories'
-import { Group } from 'common/group'
+import { CATEGORIES_GROUP_SLUG_POSTFIX } from 'common/categories'
+import { Group, GroupLink } from 'common/group'
 import { uniq } from 'lodash'
 import { Contract } from 'common/contract'
 import { User } from 'common/user'
@@ -18,28 +13,12 @@ import {
   HOUSE_LIQUIDITY_PROVIDER_ID,
 } from 'common/antes'
 
+initAdmin()
+
 const adminFirestore = admin.firestore()
 
-async function convertCategoriesToGroups() {
-  const groups = await getValues<Group>(adminFirestore.collection('groups'))
-  const contracts = await getValues<Contract>(
-    adminFirestore.collection('contracts')
-  )
-  for (const group of groups) {
-    const groupContracts = contracts.filter((contract) =>
-      group.contractIds.includes(contract.id)
-    )
-    for (const contract of groupContracts) {
-      await adminFirestore
-        .collection('contracts')
-        .doc(contract.id)
-        .update({
-          groupSlugs: uniq([...(contract.groupSlugs ?? []), group.slug]),
-        })
-    }
-  }
-
-  for (const category of Object.values(DEFAULT_CATEGORIES)) {
+const convertCategoriesToGroupsInternal = async (categories: string[]) => {
+  for (const category of categories) {
     const markets = await getValues<Contract>(
       adminFirestore
         .collection('contracts')
@@ -77,7 +56,7 @@ async function convertCategoriesToGroups() {
       createdTime: Date.now(),
       anyoneCanJoin: true,
       memberIds: [manifoldAccount],
-      about: 'Official group for all things related to ' + category,
+      about: 'Default group for all things related to ' + category,
       mostRecentActivityTime: Date.now(),
       contractIds: markets.map((market) => market.id),
       chatDisabled: true,
@@ -93,16 +72,35 @@ async function convertCategoriesToGroups() {
       })
 
     for (const market of markets) {
+      if (market.groupLinks?.map((l) => l.groupId).includes(newGroup.id))
+        continue // already in that group
+
+      const newGroupLinks = [
+        ...(market.groupLinks ?? []),
+        {
+          groupId: newGroup.id,
+          createdTime: Date.now(),
+          slug: newGroup.slug,
+          name: newGroup.name,
+        } as GroupLink,
+      ]
       await adminFirestore
         .collection('contracts')
         .doc(market.id)
         .update({
-          groupSlugs: uniq([...(market?.groupSlugs ?? []), newGroup.slug]),
+          groupSlugs: uniq([...(market.groupSlugs ?? []), newGroup.slug]),
+          groupLinks: newGroupLinks,
         })
     }
   }
 }
 
+async function convertCategoriesToGroups() {
+  // const defaultCategories = Object.values(DEFAULT_CATEGORIES)
+  const moreCategories = ['world', 'culture']
+  await convertCategoriesToGroupsInternal(moreCategories)
+}
+
 if (require.main === module) {
   convertCategoriesToGroups()
     .then(() => process.exit())
diff --git a/functions/src/scripts/link-contracts-to-groups.ts b/functions/src/scripts/link-contracts-to-groups.ts
new file mode 100644
index 00000000..e3296160
--- /dev/null
+++ b/functions/src/scripts/link-contracts-to-groups.ts
@@ -0,0 +1,53 @@
+import { getValues } from 'functions/src/utils'
+import { Group } from 'common/group'
+import { Contract } from 'common/contract'
+import { initAdmin } from 'functions/src/scripts/script-init'
+import * as admin from 'firebase-admin'
+import { filterDefined } from 'common/util/array'
+import { uniq } from 'lodash'
+
+initAdmin()
+
+const adminFirestore = admin.firestore()
+
+const addGroupIdToContracts = async () => {
+  const groups = await getValues<Group>(adminFirestore.collection('groups'))
+
+  for (const group of groups) {
+    const groupContracts = await getValues<Contract>(
+      adminFirestore
+        .collection('contracts')
+        .where('groupSlugs', 'array-contains', group.slug)
+    )
+
+    for (const contract of groupContracts) {
+      const oldGroupLinks = contract.groupLinks?.filter(
+        (l) => l.slug != group.slug
+      )
+      const newGroupLinks = filterDefined([
+        ...(oldGroupLinks ?? []),
+        group.id
+          ? {
+              slug: group.slug,
+              name: group.name,
+              groupId: group.id,
+              createdTime: Date.now(),
+            }
+          : undefined,
+      ])
+      await adminFirestore
+        .collection('contracts')
+        .doc(contract.id)
+        .update({
+          groupSlugs: uniq([...(contract.groupSlugs ?? []), group.slug]),
+          groupLinks: newGroupLinks,
+        })
+    }
+  }
+}
+
+if (require.main === module) {
+  addGroupIdToContracts()
+    .then(() => process.exit())
+    .catch(console.log)
+}
diff --git a/functions/src/sell-shares.ts b/functions/src/sell-shares.ts
index 3407760b..40ea0f4a 100644
--- a/functions/src/sell-shares.ts
+++ b/functions/src/sell-shares.ts
@@ -1,4 +1,4 @@
-import { sumBy } from 'lodash'
+import { sumBy, uniq } from 'lodash'
 import * as admin from 'firebase-admin'
 import { z } from 'zod'
 
@@ -7,11 +7,12 @@ import { Contract, CPMM_MIN_POOL_QTY } from '../../common/contract'
 import { User } from '../../common/user'
 import { getCpmmSellBetInfo } from '../../common/sell-bet'
 import { addObjects, removeUndefinedProps } from '../../common/util/object'
-import { getValues } from './utils'
+import { getValues, log } from './utils'
 import { Bet } from '../../common/bet'
 import { floatingLesserEqual } from '../../common/util/math'
 import { getUnfilledBetsQuery, updateMakers } from './place-bet'
 import { FieldValue } from 'firebase-admin/firestore'
+import { redeemShares } from './redeem-shares'
 
 const bodySchema = z.object({
   contractId: z.string(),
@@ -23,7 +24,7 @@ export const sellshares = newEndpoint({}, async (req, auth) => {
   const { contractId, shares, outcome } = validate(bodySchema, req.body)
 
   // Run as transaction to prevent race conditions.
-  return await firestore.runTransaction(async (transaction) => {
+  const result = await firestore.runTransaction(async (transaction) => {
     const contractDoc = firestore.doc(`contracts/${contractId}`)
     const userDoc = firestore.doc(`users/${auth.uid}`)
     const betsQ = contractDoc.collection('bets').where('userId', '==', auth.uid)
@@ -97,8 +98,14 @@ export const sellshares = newEndpoint({}, async (req, auth) => {
       })
     )
 
-    return { status: 'success' }
+    return { newBet, makers }
   })
+
+  const userIds = uniq(result.makers.map((maker) => maker.bet.userId))
+  await Promise.all(userIds.map((userId) => redeemShares(userId, contractId)))
+  log('Share redemption transaction finished.')
+
+  return { status: 'success' }
 })
 
 const firestore = admin.firestore()
diff --git a/web/.prettierignore b/web/.prettierignore
index b79c5513..6cc1e5c7 100644
--- a/web/.prettierignore
+++ b/web/.prettierignore
@@ -1,3 +1,4 @@
 # Ignore Next artifacts
 .next/
-out/
\ No newline at end of file
+out/
+public/**/*.json
\ No newline at end of file
diff --git a/web/components/NotificationSettings.tsx b/web/components/NotificationSettings.tsx
new file mode 100644
index 00000000..7a839a7a
--- /dev/null
+++ b/web/components/NotificationSettings.tsx
@@ -0,0 +1,210 @@
+import { useUser } from 'web/hooks/use-user'
+import React, { useEffect, useState } from 'react'
+import { notification_subscribe_types, PrivateUser } from 'common/user'
+import { listenForPrivateUser, updatePrivateUser } from 'web/lib/firebase/users'
+import toast from 'react-hot-toast'
+import { track } from '@amplitude/analytics-browser'
+import { LoadingIndicator } from 'web/components/loading-indicator'
+import { Row } from 'web/components/layout/row'
+import clsx from 'clsx'
+import { CheckIcon, XIcon } from '@heroicons/react/outline'
+import { ChoicesToggleGroup } from 'web/components/choices-toggle-group'
+
+export function NotificationSettings() {
+  const user = useUser()
+  const [notificationSettings, setNotificationSettings] =
+    useState<notification_subscribe_types>('all')
+  const [emailNotificationSettings, setEmailNotificationSettings] =
+    useState<notification_subscribe_types>('all')
+  const [privateUser, setPrivateUser] = useState<PrivateUser | null>(null)
+
+  useEffect(() => {
+    if (user) listenForPrivateUser(user.id, setPrivateUser)
+  }, [user])
+
+  useEffect(() => {
+    if (!privateUser) return
+    if (privateUser.notificationPreferences) {
+      setNotificationSettings(privateUser.notificationPreferences)
+    }
+    if (
+      privateUser.unsubscribedFromResolutionEmails &&
+      privateUser.unsubscribedFromCommentEmails &&
+      privateUser.unsubscribedFromAnswerEmails
+    ) {
+      setEmailNotificationSettings('none')
+    } else if (
+      !privateUser.unsubscribedFromResolutionEmails &&
+      !privateUser.unsubscribedFromCommentEmails &&
+      !privateUser.unsubscribedFromAnswerEmails
+    ) {
+      setEmailNotificationSettings('all')
+    } else {
+      setEmailNotificationSettings('less')
+    }
+  }, [privateUser])
+
+  const loading = 'Changing Notifications Settings'
+  const success = 'Notification Settings Changed!'
+  function changeEmailNotifications(newValue: notification_subscribe_types) {
+    if (!privateUser) return
+    if (newValue === 'all') {
+      toast.promise(
+        updatePrivateUser(privateUser.id, {
+          unsubscribedFromResolutionEmails: false,
+          unsubscribedFromCommentEmails: false,
+          unsubscribedFromAnswerEmails: false,
+        }),
+        {
+          loading,
+          success,
+          error: (err) => `${err.message}`,
+        }
+      )
+    } else if (newValue === 'less') {
+      toast.promise(
+        updatePrivateUser(privateUser.id, {
+          unsubscribedFromResolutionEmails: false,
+          unsubscribedFromCommentEmails: true,
+          unsubscribedFromAnswerEmails: true,
+        }),
+        {
+          loading,
+          success,
+          error: (err) => `${err.message}`,
+        }
+      )
+    } else if (newValue === 'none') {
+      toast.promise(
+        updatePrivateUser(privateUser.id, {
+          unsubscribedFromResolutionEmails: true,
+          unsubscribedFromCommentEmails: true,
+          unsubscribedFromAnswerEmails: true,
+        }),
+        {
+          loading,
+          success,
+          error: (err) => `${err.message}`,
+        }
+      )
+    }
+  }
+
+  function changeInAppNotificationSettings(
+    newValue: notification_subscribe_types
+  ) {
+    if (!privateUser) return
+    track('In-App Notification Preferences Changed', {
+      newPreference: newValue,
+      oldPreference: privateUser.notificationPreferences,
+    })
+    toast.promise(
+      updatePrivateUser(privateUser.id, {
+        notificationPreferences: newValue,
+      }),
+      {
+        loading,
+        success,
+        error: (err) => `${err.message}`,
+      }
+    )
+  }
+
+  useEffect(() => {
+    if (privateUser && privateUser.notificationPreferences)
+      setNotificationSettings(privateUser.notificationPreferences)
+    else setNotificationSettings('all')
+  }, [privateUser])
+
+  if (!privateUser) {
+    return <LoadingIndicator spinnerClassName={'border-gray-500 h-4 w-4'} />
+  }
+
+  function NotificationSettingLine(props: {
+    label: string
+    highlight: boolean
+  }) {
+    const { label, highlight } = props
+    return (
+      <Row className={clsx('my-1 text-gray-300', highlight && '!text-black')}>
+        {highlight ? <CheckIcon height={20} /> : <XIcon height={20} />}
+        {label}
+      </Row>
+    )
+  }
+
+  return (
+    <div className={'p-2'}>
+      <div>In App Notifications</div>
+      <ChoicesToggleGroup
+        currentChoice={notificationSettings}
+        choicesMap={{ All: 'all', Less: 'less', None: 'none' }}
+        setChoice={(choice) =>
+          changeInAppNotificationSettings(
+            choice as notification_subscribe_types
+          )
+        }
+        className={'col-span-4 p-2'}
+        toggleClassName={'w-24'}
+      />
+      <div className={'mt-4 text-sm'}>
+        <div>
+          <div className={''}>
+            You will receive notifications for:
+            <NotificationSettingLine
+              label={"Resolution of questions you've interacted with"}
+              highlight={notificationSettings !== 'none'}
+            />
+            <NotificationSettingLine
+              highlight={notificationSettings !== 'none'}
+              label={'Activity on your own questions, comments, & answers'}
+            />
+            <NotificationSettingLine
+              highlight={notificationSettings !== 'none'}
+              label={"Activity on questions you're betting on"}
+            />
+            <NotificationSettingLine
+              highlight={notificationSettings !== 'none'}
+              label={"Income & referral bonuses you've received"}
+            />
+            <NotificationSettingLine
+              label={"Activity on questions you've ever bet or commented on"}
+              highlight={notificationSettings === 'all'}
+            />
+          </div>
+        </div>
+      </div>
+      <div className={'mt-4'}>Email Notifications</div>
+      <ChoicesToggleGroup
+        currentChoice={emailNotificationSettings}
+        choicesMap={{ All: 'all', Less: 'less', None: 'none' }}
+        setChoice={(choice) =>
+          changeEmailNotifications(choice as notification_subscribe_types)
+        }
+        className={'col-span-4 p-2'}
+        toggleClassName={'w-24'}
+      />
+      <div className={'mt-4 text-sm'}>
+        <div>
+          You will receive emails for:
+          <NotificationSettingLine
+            label={"Resolution of questions you're betting on"}
+            highlight={emailNotificationSettings !== 'none'}
+          />
+          <NotificationSettingLine
+            label={'Closure of your questions'}
+            highlight={emailNotificationSettings !== 'none'}
+          />
+          <NotificationSettingLine
+            label={'Activity on your questions'}
+            highlight={emailNotificationSettings === 'all'}
+          />
+          <NotificationSettingLine
+            label={"Activity on questions you've answered or commented on"}
+            highlight={emailNotificationSettings === 'all'}
+          />
+        </div>
+      </div>
+    </div>
+  )
+}
diff --git a/web/components/amount-input.tsx b/web/components/amount-input.tsx
index a31957cb..426a9371 100644
--- a/web/components/amount-input.tsx
+++ b/web/components/amount-input.tsx
@@ -41,7 +41,7 @@ export function AmountInput(props: {
         <span className="bg-gray-200 text-sm">{label}</span>
         <input
           className={clsx(
-            'input input-bordered max-w-[200px] text-lg',
+            'input input-bordered max-w-[200px] text-lg placeholder:text-gray-400',
             error && 'input-error',
             inputClassName
           )}
diff --git a/web/components/auth-context.tsx b/web/components/auth-context.tsx
new file mode 100644
index 00000000..653368b6
--- /dev/null
+++ b/web/components/auth-context.tsx
@@ -0,0 +1,77 @@
+import { createContext, useEffect } from 'react'
+import { User } from 'common/user'
+import { onIdTokenChanged } from 'firebase/auth'
+import {
+  auth,
+  listenForUser,
+  getUser,
+  setCachedReferralInfoForUser,
+} from 'web/lib/firebase/users'
+import { deleteAuthCookies, setAuthCookies } from 'web/lib/firebase/auth'
+import { createUser } from 'web/lib/firebase/api'
+import { randomString } from 'common/util/random'
+import { identifyUser, setUserProperty } from 'web/lib/service/analytics'
+import { useStateCheckEquality } from 'web/hooks/use-state-check-equality'
+
+// Either we haven't looked up the logged in user yet (undefined), or we know
+// the user is not logged in (null), or we know the user is logged in (User).
+type AuthUser = undefined | null | User
+
+const CACHED_USER_KEY = 'CACHED_USER_KEY'
+
+const ensureDeviceToken = () => {
+  let deviceToken = localStorage.getItem('device-token')
+  if (!deviceToken) {
+    deviceToken = randomString()
+    localStorage.setItem('device-token', deviceToken)
+  }
+  return deviceToken
+}
+
+export const AuthContext = createContext<AuthUser>(null)
+
+export function AuthProvider({ children }: any) {
+  const [authUser, setAuthUser] = useStateCheckEquality<AuthUser>(undefined)
+
+  useEffect(() => {
+    const cachedUser = localStorage.getItem(CACHED_USER_KEY)
+    setAuthUser(cachedUser && JSON.parse(cachedUser))
+  }, [setAuthUser])
+
+  useEffect(() => {
+    return onIdTokenChanged(auth, async (fbUser) => {
+      if (fbUser) {
+        setAuthCookies(await fbUser.getIdToken(), fbUser.refreshToken)
+        let user = await getUser(fbUser.uid)
+        if (!user) {
+          const deviceToken = ensureDeviceToken()
+          user = (await createUser({ deviceToken })) as User
+        }
+        setAuthUser(user)
+        // Persist to local storage, to reduce login blink next time.
+        // Note: Cap on localStorage size is ~5mb
+        localStorage.setItem(CACHED_USER_KEY, JSON.stringify(user))
+        setCachedReferralInfoForUser(user)
+      } else {
+        // User logged out; reset to null
+        deleteAuthCookies()
+        setAuthUser(null)
+        localStorage.removeItem(CACHED_USER_KEY)
+      }
+    })
+  }, [setAuthUser])
+
+  const authUserId = authUser?.id
+  const authUsername = authUser?.username
+  useEffect(() => {
+    if (authUserId && authUsername) {
+      identifyUser(authUserId)
+      setUserProperty('username', authUsername)
+      return listenForUser(authUserId, setAuthUser)
+    }
+  }, [authUserId, authUsername, setAuthUser])
+
+  return (
+    <AuthContext.Provider value={authUser}>{children}</AuthContext.Provider>
+  )
+}
diff --git a/web/components/avatar.tsx b/web/components/avatar.tsx
index 53257deb..0436d61c 100644
--- a/web/components/avatar.tsx
+++ b/web/components/avatar.tsx
@@ -31,6 +31,7 @@ export function Avatar(props: {
         !noLink && 'cursor-pointer',
         className
       )}
+      style={{ maxWidth: `${s * 0.25}rem` }}
       src={avatarUrl}
       onClick={onClick}
       alt={username}
diff --git a/web/components/bet-panel.tsx b/web/components/bet-panel.tsx
index 8343d696..c638fcde 100644
--- a/web/components/bet-panel.tsx
+++ b/web/components/bet-panel.tsx
@@ -1,6 +1,6 @@
 import clsx from 'clsx'
 import React, { useEffect, useState } from 'react'
-import { partition, sum, sumBy } from 'lodash'
+import { clamp, partition, sum, sumBy } from 'lodash'
 
 import { useUser } from 'web/hooks/use-user'
 import { CPMMBinaryContract, PseudoNumericContract } from 'common/contract'
@@ -13,33 +13,35 @@ import {
   formatPercent,
   formatWithCommas,
 } from 'common/util/format'
-import { getBinaryCpmmBetInfo } from 'common/new-bet'
+import { getBinaryBetStats, getBinaryCpmmBetInfo } from 'common/new-bet'
 import { User } from 'web/lib/firebase/users'
 import { Bet, LimitBet } from 'common/bet'
 import { APIError, placeBet } from 'web/lib/firebase/api'
 import { sellShares } from 'web/lib/firebase/api'
 import { AmountInput, BuyAmountInput } from './amount-input'
 import { InfoTooltip } from './info-tooltip'
-import { BinaryOutcomeLabel } from './outcome-label'
+import {
+  BinaryOutcomeLabel,
+  HigherLabel,
+  LowerLabel,
+  NoLabel,
+  YesLabel,
+} from './outcome-label'
 import { getProbability } from 'common/calculate'
 import { useFocus } from 'web/hooks/use-focus'
 import { useUserContractBets } from 'web/hooks/use-user-bets'
 import { calculateCpmmSale, getCpmmProbability } from 'common/calculate-cpmm'
-import {
-  getFormattedMappedValue,
-  getPseudoProbability,
-} from 'common/pseudo-numeric'
+import { getFormattedMappedValue } from 'common/pseudo-numeric'
 import { SellRow } from './sell-row'
 import { useSaveBinaryShares } from './use-save-binary-shares'
 import { SignUpPrompt } from './sign-up-prompt'
 import { isIOS } from 'web/lib/util/device'
-import { ProbabilityInput } from './probability-input'
+import { ProbabilityOrNumericInput } from './probability-input'
 import { track } from 'web/lib/service/analytics'
-import { removeUndefinedProps } from 'common/util/object'
 import { useUnfilledBets } from 'web/hooks/use-bets'
 import { LimitBets } from './limit-bets'
-import { BucketInput } from './bucket-input'
 import { PillButton } from './buttons/pill-button'
+import { YesNoSelector } from './yes-no-selector'
 
 export function BetPanel(props: {
   contract: CPMMBinaryContract | PseudoNumericContract
@@ -49,14 +51,10 @@ export function BetPanel(props: {
   const user = useUser()
   const userBets = useUserContractBets(user?.id, contract.id)
   const unfilledBets = useUnfilledBets(contract.id) ?? []
-  const yourUnfilledBets = unfilledBets.filter((bet) => bet.userId === user?.id)
   const { sharesOutcome } = useSaveBinaryShares(contract, userBets)
 
   const [isLimitOrder, setIsLimitOrder] = useState(false)
 
-  const showLimitOrders =
-    (isLimitOrder && unfilledBets.length > 0) || yourUnfilledBets.length > 0
-
   return (
     <Col className={className}>
       <SellRow
@@ -76,15 +74,20 @@ export function BetPanel(props: {
           setIsLimitOrder={setIsLimitOrder}
         />
         <BuyPanel
+          hidden={isLimitOrder}
+          contract={contract}
+          user={user}
+          unfilledBets={unfilledBets}
+        />
+        <LimitOrderPanel
+          hidden={!isLimitOrder}
           contract={contract}
           user={user}
-          isLimitOrder={isLimitOrder}
           unfilledBets={unfilledBets}
         />
-
         <SignUpPrompt />
       </Col>
-      {showLimitOrders && (
+      {unfilledBets.length > 0 && (
         <LimitBets className="mt-4" contract={contract} bets={unfilledBets} />
       )}
     </Col>
@@ -104,9 +107,6 @@ export function SimpleBetPanel(props: {
   const [isLimitOrder, setIsLimitOrder] = useState(false)
 
   const unfilledBets = useUnfilledBets(contract.id) ?? []
-  const yourUnfilledBets = unfilledBets.filter((bet) => bet.userId === user?.id)
-  const showLimitOrders =
-    (isLimitOrder && unfilledBets.length > 0) || yourUnfilledBets.length > 0
 
   return (
     <Col className={className}>
@@ -126,18 +126,24 @@ export function SimpleBetPanel(props: {
           setIsLimitOrder={setIsLimitOrder}
         />
         <BuyPanel
+          hidden={isLimitOrder}
           contract={contract}
           user={user}
           unfilledBets={unfilledBets}
           selected={selected}
           onBuySuccess={onBetSuccess}
-          isLimitOrder={isLimitOrder}
         />
-
+        <LimitOrderPanel
+          hidden={!isLimitOrder}
+          contract={contract}
+          user={user}
+          unfilledBets={unfilledBets}
+          onBuySuccess={onBetSuccess}
+        />
         <SignUpPrompt />
       </Col>
 
-      {showLimitOrders && (
+      {unfilledBets.length > 0 && (
         <LimitBets className="mt-4" contract={contract} bets={unfilledBets} />
       )}
     </Col>
@@ -148,21 +154,17 @@ function BuyPanel(props: {
   contract: CPMMBinaryContract | PseudoNumericContract
   user: User | null | undefined
   unfilledBets: Bet[]
-  isLimitOrder?: boolean
+  hidden: boolean
   selected?: 'YES' | 'NO'
   onBuySuccess?: () => void
 }) {
-  const { contract, user, unfilledBets, isLimitOrder, selected, onBuySuccess } =
-    props
+  const { contract, user, unfilledBets, hidden, selected, onBuySuccess } = props
 
   const initialProb = getProbability(contract)
   const isPseudoNumeric = contract.outcomeType === 'PSEUDO_NUMERIC'
 
-  const [betChoice, setBetChoice] = useState<'YES' | 'NO' | undefined>(selected)
+  const [outcome, setOutcome] = useState<'YES' | 'NO' | undefined>(selected)
   const [betAmount, setBetAmount] = useState<number | undefined>(undefined)
-  const [limitProb, setLimitProb] = useState<number | undefined>(
-    Math.round(100 * initialProb)
-  )
   const [error, setError] = useState<string | undefined>()
   const [isSubmitting, setIsSubmitting] = useState(false)
   const [wasSubmitted, setWasSubmitted] = useState(false)
@@ -177,7 +179,7 @@ function BuyPanel(props: {
   }, [selected, focusAmountInput])
 
   function onBetChoice(choice: 'YES' | 'NO') {
-    setBetChoice(choice)
+    setOutcome(choice)
     setWasSubmitted(false)
     focusAmountInput()
   }
@@ -185,29 +187,22 @@ function BuyPanel(props: {
   function onBetChange(newAmount: number | undefined) {
     setWasSubmitted(false)
     setBetAmount(newAmount)
-    if (!betChoice) {
-      setBetChoice('YES')
+    if (!outcome) {
+      setOutcome('YES')
     }
   }
 
   async function submitBet() {
     if (!user || !betAmount) return
-    if (isLimitOrder && limitProb === undefined) return
-
-    const limitProbScaled =
-      isLimitOrder && limitProb !== undefined ? limitProb / 100 : undefined
 
     setError(undefined)
     setIsSubmitting(true)
 
-    placeBet(
-      removeUndefinedProps({
-        amount: betAmount,
-        outcome: betChoice,
-        contractId: contract.id,
-        limitProb: limitProbScaled,
-      })
-    )
+    placeBet({
+      outcome,
+      amount: betAmount,
+      contractId: contract.id,
+    })
       .then((r) => {
         console.log('placed bet. Result:', r)
         setIsSubmitting(false)
@@ -231,21 +226,18 @@ function BuyPanel(props: {
       slug: contract.slug,
       contractId: contract.id,
       amount: betAmount,
-      outcome: betChoice,
-      isLimitOrder,
-      limitProb: limitProbScaled,
+      outcome,
+      isLimitOrder: false,
     })
   }
 
   const betDisabled = isSubmitting || !betAmount || error
 
-  const limitProbFrac = (limitProb ?? 0) / 100
-
   const { newPool, newP, newBet } = getBinaryCpmmBetInfo(
-    betChoice ?? 'YES',
+    outcome ?? 'YES',
     betAmount ?? 0,
     contract,
-    isLimitOrder ? limitProbFrac : undefined,
+    undefined,
     unfilledBets as LimitBet[]
   )
 
@@ -253,11 +245,7 @@ function BuyPanel(props: {
   const probStayedSame =
     formatPercent(resultProb) === formatPercent(initialProb)
 
-  const remainingMatched = isLimitOrder
-    ? ((newBet.orderAmount ?? 0) - newBet.amount) /
-      (betChoice === 'YES' ? limitProbFrac : 1 - limitProbFrac)
-    : 0
-  const currentPayout = newBet.shares + remainingMatched
+  const currentPayout = newBet.shares
 
   const currentReturn = betAmount ? (currentPayout - betAmount) / betAmount : 0
   const currentReturnPercent = formatPercent(currentReturn)
@@ -267,26 +255,17 @@ function BuyPanel(props: {
   const format = getFormattedMappedValue(contract)
 
   return (
-    <>
-      <div className="my-3 text-left text-sm text-gray-500">Direction</div>
-      <Row className="mb-4 items-center gap-2">
-        <PillButton
-          selected={betChoice === 'YES'}
-          onSelect={() => onBetChoice('YES')}
-          big
-          color="bg-primary"
-        >
-          {isPseudoNumeric ? 'Higher' : 'Yes'}
-        </PillButton>
-        <PillButton
-          selected={betChoice === 'NO'}
-          onSelect={() => onBetChoice('NO')}
-          big
-          color="bg-red-400"
-        >
-          {isPseudoNumeric ? 'Lower' : 'No'}
-        </PillButton>
-      </Row>
+    <Col className={hidden ? 'hidden' : ''}>
+      <div className="my-3 text-left text-sm text-gray-500">
+        {isPseudoNumeric ? 'Direction' : 'Outcome'}
+      </div>
+      <YesNoSelector
+        className="mb-4"
+        btnClassName="flex-1"
+        selected={outcome}
+        onSelect={(choice) => onBetChoice(choice)}
+        isPseudoNumeric={isPseudoNumeric}
+      />
 
       <div className="my-3 text-left text-sm text-gray-500">Amount</div>
       <BuyAmountInput
@@ -298,61 +277,21 @@ function BuyPanel(props: {
         disabled={isSubmitting}
         inputRef={inputRef}
       />
-      {isLimitOrder && (
-        <>
-          <Row className="my-3 items-center gap-2 text-left text-sm text-gray-500">
-            Limit {isPseudoNumeric ? 'value' : 'probability'}
-            <InfoTooltip
-              text={`Bet ${betChoice === 'NO' ? 'down' : 'up'} to this ${
-                isPseudoNumeric ? 'value' : 'probability'
-              } and wait to match other bets.`}
-            />
-          </Row>
-          {isPseudoNumeric ? (
-            <BucketInput
-              contract={contract}
-              onBucketChange={(value) =>
-                setLimitProb(
-                  value === undefined
-                    ? undefined
-                    : 100 *
-                        getPseudoProbability(
-                          value,
-                          contract.min,
-                          contract.max,
-                          contract.isLogScale
-                        )
-                )
-              }
-              isSubmitting={isSubmitting}
-            />
-          ) : (
-            <ProbabilityInput
-              inputClassName="w-full max-w-none"
-              prob={limitProb}
-              onChange={setLimitProb}
-              disabled={isSubmitting}
-            />
-          )}
-        </>
-      )}
       <Col className="mt-3 w-full gap-3">
-        {!isLimitOrder && (
-          <Row className="items-center justify-between text-sm">
-            <div className="text-gray-500">
-              {isPseudoNumeric ? 'Estimated value' : 'Probability'}
+        <Row className="items-center justify-between text-sm">
+          <div className="text-gray-500">
+            {isPseudoNumeric ? 'Estimated value' : 'Probability'}
+          </div>
+          {probStayedSame ? (
+            <div>{format(initialProb)}</div>
+          ) : (
+            <div>
+              {format(initialProb)}
+              <span className="mx-2">→</span>
+              {format(resultProb)}
             </div>
-            {probStayedSame ? (
-              <div>{format(initialProb)}</div>
-            ) : (
-              <div>
-                {format(initialProb)}
-                <span className="mx-2">→</span>
-                {format(resultProb)}
-              </div>
-            )}
-          </Row>
-        )}
+          )}
+        </Row>
 
         <Row className="items-center justify-between gap-2 text-sm">
           <Row className="flex-nowrap items-center gap-2 whitespace-nowrap text-gray-500">
@@ -361,7 +300,7 @@ function BuyPanel(props: {
                 'Max payout'
               ) : (
                 <>
-                  Payout if <BinaryOutcomeLabel outcome={betChoice ?? 'YES'} />
+                  Payout if <BinaryOutcomeLabel outcome={outcome ?? 'YES'} />
                 </>
               )}
             </div>
@@ -386,7 +325,7 @@ function BuyPanel(props: {
             'btn flex-1',
             betDisabled
               ? 'btn-disabled'
-              : betChoice === 'YES'
+              : outcome === 'YES'
               ? 'btn-primary'
               : 'border-none bg-red-400 hover:bg-red-500',
             isSubmitting ? 'loading' : ''
@@ -397,10 +336,352 @@ function BuyPanel(props: {
         </button>
       )}
 
-      {wasSubmitted && (
-        <div className="mt-4">{isLimitOrder ? 'Order' : 'Bet'} submitted!</div>
+      {wasSubmitted && <div className="mt-4">Bet submitted!</div>}
+    </Col>
+  )
+}
+
+function LimitOrderPanel(props: {
+  contract: CPMMBinaryContract | PseudoNumericContract
+  user: User | null | undefined
+  unfilledBets: Bet[]
+  hidden: boolean
+  onBuySuccess?: () => void
+}) {
+  const { contract, user, unfilledBets, hidden, onBuySuccess } = props
+
+  const initialProb = getProbability(contract)
+  const isPseudoNumeric = contract.outcomeType === 'PSEUDO_NUMERIC'
+
+  const [betAmount, setBetAmount] = useState<number | undefined>(undefined)
+  const [lowLimitProb, setLowLimitProb] = useState<number | undefined>()
+  const [highLimitProb, setHighLimitProb] = useState<number | undefined>()
+  const betChoice = 'YES'
+  const [error, setError] = useState<string | undefined>()
+  const [isSubmitting, setIsSubmitting] = useState(false)
+  const [wasSubmitted, setWasSubmitted] = useState(false)
+
+  const rangeError =
+    lowLimitProb !== undefined &&
+    highLimitProb !== undefined &&
+    lowLimitProb >= highLimitProb
+
+  const outOfRangeError =
+    (lowLimitProb !== undefined &&
+      (lowLimitProb <= 0 || lowLimitProb >= 100)) ||
+    (highLimitProb !== undefined &&
+      (highLimitProb <= 0 || highLimitProb >= 100))
+
+  const hasYesLimitBet = lowLimitProb !== undefined && !!betAmount
+  const hasNoLimitBet = highLimitProb !== undefined && !!betAmount
+  const hasTwoBets = hasYesLimitBet && hasNoLimitBet
+
+  const betDisabled =
+    isSubmitting ||
+    !betAmount ||
+    rangeError ||
+    outOfRangeError ||
+    error ||
+    (!hasYesLimitBet && !hasNoLimitBet)
+
+  const yesLimitProb =
+    lowLimitProb === undefined
+      ? undefined
+      : clamp(lowLimitProb / 100, 0.001, 0.999)
+  const noLimitProb =
+    highLimitProb === undefined
+      ? undefined
+      : clamp(highLimitProb / 100, 0.001, 0.999)
+
+  const amount = betAmount ?? 0
+  const shares =
+    yesLimitProb !== undefined && noLimitProb !== undefined
+      ? Math.min(amount / yesLimitProb, amount / (1 - noLimitProb))
+      : yesLimitProb !== undefined
+      ? amount / yesLimitProb
+      : noLimitProb !== undefined
+      ? amount / (1 - noLimitProb)
+      : 0
+
+  const yesAmount = shares * (yesLimitProb ?? 1)
+  const noAmount = shares * (1 - (noLimitProb ?? 0))
+
+  const profitIfBothFilled = shares - (yesAmount + noAmount)
+
+  function onBetChange(newAmount: number | undefined) {
+    setWasSubmitted(false)
+    setBetAmount(newAmount)
+  }
+
+  async function submitBet() {
+    if (!user || betDisabled) return
+
+    setError(undefined)
+    setIsSubmitting(true)
+
+    const betsPromise = hasTwoBets
+      ? Promise.all([
+          placeBet({
+            outcome: 'YES',
+            amount: yesAmount,
+            limitProb: yesLimitProb,
+            contractId: contract.id,
+          }),
+          placeBet({
+            outcome: 'NO',
+            amount: noAmount,
+            limitProb: noLimitProb,
+            contractId: contract.id,
+          }),
+        ])
+      : placeBet({
+          outcome: hasYesLimitBet ? 'YES' : 'NO',
+          amount: betAmount,
+          contractId: contract.id,
+          limitProb: hasYesLimitBet ? yesLimitProb : noLimitProb,
+        })
+
+    betsPromise
+      .catch((e) => {
+        if (e instanceof APIError) {
+          setError(e.toString())
+        } else {
+          console.error(e)
+          setError('Error placing bet')
+        }
+        setIsSubmitting(false)
+      })
+      .then((r) => {
+        console.log('placed bet. Result:', r)
+        setIsSubmitting(false)
+        setWasSubmitted(true)
+        setBetAmount(undefined)
+        if (onBuySuccess) onBuySuccess()
+      })
+
+    if (hasYesLimitBet) {
+      track('bet', {
+        location: 'bet panel',
+        outcomeType: contract.outcomeType,
+        slug: contract.slug,
+        contractId: contract.id,
+        amount: yesAmount,
+        outcome: 'YES',
+        limitProb: yesLimitProb,
+        isLimitOrder: true,
+        isRangeOrder: hasTwoBets,
+      })
+    }
+    if (hasNoLimitBet) {
+      track('bet', {
+        location: 'bet panel',
+        outcomeType: contract.outcomeType,
+        slug: contract.slug,
+        contractId: contract.id,
+        amount: noAmount,
+        outcome: 'NO',
+        limitProb: noLimitProb,
+        isLimitOrder: true,
+        isRangeOrder: hasTwoBets,
+      })
+    }
+  }
+
+  const {
+    currentPayout: yesPayout,
+    currentReturn: yesReturn,
+    totalFees: yesFees,
+    newBet: yesBet,
+  } = getBinaryBetStats(
+    'YES',
+    yesAmount,
+    contract,
+    yesLimitProb ?? initialProb,
+    unfilledBets as LimitBet[]
+  )
+  const yesReturnPercent = formatPercent(yesReturn)
+
+  const {
+    currentPayout: noPayout,
+    currentReturn: noReturn,
+    totalFees: noFees,
+    newBet: noBet,
+  } = getBinaryBetStats(
+    'NO',
+    noAmount,
+    contract,
+    noLimitProb ?? initialProb,
+    unfilledBets as LimitBet[]
+  )
+  const noReturnPercent = formatPercent(noReturn)
+
+  return (
+    <Col className={hidden ? 'hidden' : ''}>
+      <Row className="mt-1 items-center gap-4">
+        <Col className="gap-2">
+          <div className="relative ml-1 text-sm text-gray-500">
+            Bet {isPseudoNumeric ? <HigherLabel /> : <YesLabel />} at
+          </div>
+          <ProbabilityOrNumericInput
+            contract={contract}
+            prob={lowLimitProb}
+            setProb={setLowLimitProb}
+            isSubmitting={isSubmitting}
+          />
+        </Col>
+        <Col className="gap-2">
+          <div className="ml-1 text-sm text-gray-500">
+            Bet {isPseudoNumeric ? <LowerLabel /> : <NoLabel />} at
+          </div>
+          <ProbabilityOrNumericInput
+            contract={contract}
+            prob={highLimitProb}
+            setProb={setHighLimitProb}
+            isSubmitting={isSubmitting}
+          />
+        </Col>
+      </Row>
+
+      {outOfRangeError && (
+        <div className="mb-2 mr-auto self-center whitespace-nowrap text-xs font-medium tracking-wide text-red-500">
+          Limit is out of range
+        </div>
       )}
-    </>
+      {rangeError && !outOfRangeError && (
+        <div className="mb-2 mr-auto self-center whitespace-nowrap text-xs font-medium tracking-wide text-red-500">
+          {isPseudoNumeric ? 'HIGHER' : 'YES'} limit must be less than{' '}
+          {isPseudoNumeric ? 'LOWER' : 'NO'} limit
+        </div>
+      )}
+
+      <div className="mt-1 mb-3 text-left text-sm text-gray-500">
+        Max amount<span className="ml-1 text-red-500">*</span>
+      </div>
+      <BuyAmountInput
+        inputClassName="w-full max-w-none"
+        amount={betAmount}
+        onChange={onBetChange}
+        error={error}
+        setError={setError}
+        disabled={isSubmitting}
+      />
+
+      <Col className="mt-3 w-full gap-3">
+        {(hasTwoBets || (hasYesLimitBet && yesBet.amount !== 0)) && (
+          <Row className="items-center justify-between gap-2 text-sm">
+            <div className="whitespace-nowrap text-gray-500">
+              {isPseudoNumeric ? (
+                <HigherLabel />
+              ) : (
+                <BinaryOutcomeLabel outcome={'YES'} />
+              )}{' '}
+              filled now
+            </div>
+            <div className="mr-2 whitespace-nowrap">
+              {formatMoney(yesBet.amount)} of{' '}
+              {formatMoney(yesBet.orderAmount ?? 0)}
+            </div>
+          </Row>
+        )}
+        {(hasTwoBets || (hasNoLimitBet && noBet.amount !== 0)) && (
+          <Row className="items-center justify-between gap-2 text-sm">
+            <div className="whitespace-nowrap text-gray-500">
+              {isPseudoNumeric ? (
+                <LowerLabel />
+              ) : (
+                <BinaryOutcomeLabel outcome={'NO'} />
+              )}{' '}
+              filled now
+            </div>
+            <div className="mr-2 whitespace-nowrap">
+              {formatMoney(noBet.amount)} of{' '}
+              {formatMoney(noBet.orderAmount ?? 0)}
+            </div>
+          </Row>
+        )}
+        {hasTwoBets && (
+          <Row className="items-center justify-between gap-2 text-sm">
+            <div className="whitespace-nowrap text-gray-500">
+              Profit if both orders filled
+            </div>
+            <div className="mr-2 whitespace-nowrap">
+              {formatMoney(profitIfBothFilled)}
+            </div>
+          </Row>
+        )}
+        {hasYesLimitBet && !hasTwoBets && (
+          <Row className="items-center justify-between gap-2 text-sm">
+            <Row className="flex-nowrap items-center gap-2 whitespace-nowrap text-gray-500">
+              <div>
+                {isPseudoNumeric ? (
+                  'Max payout'
+                ) : (
+                  <>
+                    Max <BinaryOutcomeLabel outcome={'YES'} /> payout
+                  </>
+                )}
+              </div>
+              <InfoTooltip
+                text={`Includes ${formatMoneyWithDecimals(yesFees)} in fees`}
+              />
+            </Row>
+            <div>
+              <span className="mr-2 whitespace-nowrap">
+                {formatMoney(yesPayout)}
+              </span>
+              (+{yesReturnPercent})
+            </div>
+          </Row>
+        )}
+        {hasNoLimitBet && !hasTwoBets && (
+          <Row className="items-center justify-between gap-2 text-sm">
+            <Row className="flex-nowrap items-center gap-2 whitespace-nowrap text-gray-500">
+              <div>
+                {isPseudoNumeric ? (
+                  'Max payout'
+                ) : (
+                  <>
+                    Max <BinaryOutcomeLabel outcome={'NO'} /> payout
+                  </>
+                )}
+              </div>
+              <InfoTooltip
+                text={`Includes ${formatMoneyWithDecimals(noFees)} in fees`}
+              />
+            </Row>
+            <div>
+              <span className="mr-2 whitespace-nowrap">
+                {formatMoney(noPayout)}
+              </span>
+              (+{noReturnPercent})
+            </div>
+          </Row>
+        )}
+      </Col>
+
+      {(hasYesLimitBet || hasNoLimitBet) && <Spacer h={8} />}
+
+      {user && (
+        <button
+          className={clsx(
+            'btn flex-1',
+            betDisabled
+              ? 'btn-disabled'
+              : betChoice === 'YES'
+              ? 'btn-primary'
+              : 'border-none bg-red-400 hover:bg-red-500',
+            isSubmitting ? 'loading' : ''
+          )}
+          onClick={betDisabled ? undefined : submitBet}
+        >
+          {isSubmitting
+            ? 'Submitting...'
+            : `Submit order${hasTwoBets ? 's' : ''}`}
+        </button>
+      )}
+
+      {wasSubmitted && <div className="mt-4">Order submitted!</div>}
+    </Col>
   )
 }
 
@@ -413,7 +694,7 @@ function QuickOrLimitBet(props: {
   return (
     <Row className="align-center mb-4 justify-between">
       <div className="text-4xl">Bet</div>
-      <Row className="mt-2 items-center gap-2">
+      <Row className="mt-1 items-center gap-2">
         <PillButton
           selected={!isLimitOrder}
           onSelect={() => {
diff --git a/web/components/bets-list.tsx b/web/components/bets-list.tsx
index db6b0d05..a306a020 100644
--- a/web/components/bets-list.tsx
+++ b/web/components/bets-list.tsx
@@ -50,7 +50,7 @@ import { LimitOrderTable } from './limit-bets'
 type BetSort = 'newest' | 'profit' | 'closeTime' | 'value'
 type BetFilter = 'open' | 'limit_bet' | 'sold' | 'closed' | 'resolved' | 'all'
 
-const CONTRACTS_PER_PAGE = 20
+const CONTRACTS_PER_PAGE = 50
 
 export function BetsList(props: {
   user: User
@@ -78,10 +78,10 @@ export function BetsList(props: {
 
   const getTime = useTimeSinceFirstRender()
   useEffect(() => {
-    if (bets && contractsById) {
-      trackLatency('portfolio', getTime())
+    if (bets && contractsById && signedInUser) {
+      trackLatency(signedInUser.id, 'portfolio', getTime())
     }
-  }, [bets, contractsById, getTime])
+  }, [signedInUser, bets, contractsById, getTime])
 
   if (!bets || !contractsById) {
     return <LoadingIndicator />
diff --git a/web/components/bucket-input.tsx b/web/components/bucket-input.tsx
index 195032dc..19dacd65 100644
--- a/web/components/bucket-input.tsx
+++ b/web/components/bucket-input.tsx
@@ -9,8 +9,9 @@ export function BucketInput(props: {
   contract: NumericContract | PseudoNumericContract
   isSubmitting?: boolean
   onBucketChange: (value?: number, bucket?: string) => void
+  placeholder?: string
 }) {
-  const { contract, isSubmitting, onBucketChange } = props
+  const { contract, isSubmitting, onBucketChange, placeholder } = props
 
   const [numberString, setNumberString] = useState('')
 
@@ -39,7 +40,7 @@ export function BucketInput(props: {
       error={undefined}
       disabled={isSubmitting}
       numberString={numberString}
-      label="Value"
+      placeholder={placeholder}
     />
   )
 }
diff --git a/web/components/button.tsx b/web/components/button.tsx
index 3b59581b..8877c023 100644
--- a/web/components/button.tsx
+++ b/web/components/button.tsx
@@ -1,34 +1,49 @@
 import { ReactNode } from 'react'
 import clsx from 'clsx'
 
-export default function Button(props: {
+export function Button(props: {
   className?: string
   onClick?: () => void
-  color: 'green' | 'red' | 'blue' | 'indigo' | 'yellow' | 'gray'
   children?: ReactNode
+  size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'
+  color?: 'green' | 'red' | 'blue' | 'indigo' | 'yellow' | 'gray' | 'gray-white'
   type?: 'button' | 'reset' | 'submit'
+  disabled?: boolean
 }) {
   const {
+    children,
     className,
     onClick,
-    children,
+    size = 'md',
     color = 'indigo',
     type = 'button',
+    disabled = false,
   } = props
 
+  const sizeClasses = {
+    xs: 'px-2.5 py-1.5 text-sm',
+    sm: 'px-3 py-2 text-sm',
+    md: 'px-4 py-2 text-sm',
+    lg: 'px-4 py-2 text-base',
+    xl: 'px-6 py-3 text-base',
+  }[size]
+
   return (
     <button
       type={type}
       className={clsx(
-        'font-md items-center justify-center rounded-md border border-transparent px-4 py-2 shadow-sm hover:transition-colors',
+        'font-md items-center justify-center rounded-md border border-transparent shadow-sm hover:transition-colors disabled:cursor-not-allowed disabled:opacity-50',
+        sizeClasses,
         color === 'green' && 'btn-primary text-white',
         color === 'red' && 'bg-red-400 text-white hover:bg-red-500',
         color === 'yellow' && 'bg-yellow-400 text-white hover:bg-yellow-500',
         color === 'blue' && 'bg-blue-400 text-white hover:bg-blue-500',
         color === 'indigo' && 'bg-indigo-500 text-white hover:bg-indigo-600',
-        color === 'gray' && 'bg-gray-200 text-gray-700 hover:bg-gray-300',
+        color === 'gray' && 'bg-gray-100 text-gray-600 hover:bg-gray-200',
+        color === 'gray-white' && 'bg-white text-gray-500 hover:bg-gray-200',
         className
       )}
+      disabled={disabled}
       onClick={onClick}
     >
       {children}
diff --git a/web/components/buttons/pill-button.tsx b/web/components/buttons/pill-button.tsx
index 796036d1..5b4962b7 100644
--- a/web/components/buttons/pill-button.tsx
+++ b/web/components/buttons/pill-button.tsx
@@ -13,7 +13,7 @@ export function PillButton(props: {
   return (
     <button
       className={clsx(
-        'cursor-pointer select-none rounded-full',
+        'cursor-pointer select-none whitespace-nowrap rounded-full',
         selected
           ? ['text-white', color ?? 'bg-gray-700']
           : 'bg-gray-100 hover:bg-gray-200',
diff --git a/web/components/charity/charity-card.tsx b/web/components/charity/charity-card.tsx
index 31995284..fc327b9f 100644
--- a/web/components/charity/charity-card.tsx
+++ b/web/components/charity/charity-card.tsx
@@ -6,10 +6,9 @@ import { Charity } from 'common/charity'
 import { useCharityTxns } from 'web/hooks/use-charity-txns'
 import { manaToUSD } from '../../../common/util/format'
 import { Row } from '../layout/row'
-import { Col } from '../layout/col'
 
 export function CharityCard(props: { charity: Charity; match?: number }) {
-  const { charity, match } = props
+  const { charity } = props
   const { slug, photo, preview, id, tags } = charity
 
   const txns = useCharityTxns(id)
@@ -36,18 +35,18 @@ export function CharityCard(props: { charity: Charity; match?: number }) {
           {raised > 0 && (
             <>
               <Row className="mt-4 flex-1 items-end justify-center gap-6 text-gray-900">
-                <Col>
+                <Row className="items-baseline gap-1">
                   <span className="text-3xl font-semibold">
                     {formatUsd(raised)}
                   </span>
-                  <span>raised</span>
-                </Col>
-                {match && (
+                  raised
+                </Row>
+                {/* {match && (
                   <Col className="text-gray-500">
                     <span className="text-xl">+{formatUsd(match)}</span>
                     <span className="">match</span>
                   </Col>
-                )}
+                )} */}
               </Row>
             </>
           )}
diff --git a/web/components/contract-search.tsx b/web/components/contract-search.tsx
index 952d4034..45145c54 100644
--- a/web/components/contract-search.tsx
+++ b/web/components/contract-search.tsx
@@ -22,10 +22,13 @@ import { Spacer } from './layout/spacer'
 import { ENV, IS_PRIVATE_MANIFOLD } from 'common/envs/constants'
 import { useUser } from 'web/hooks/use-user'
 import { useFollows } from 'web/hooks/use-follows'
-import { trackCallback } from 'web/lib/service/analytics'
+import { track, trackCallback } from 'web/lib/service/analytics'
 import ContractSearchFirestore from 'web/pages/contract-search-firestore'
 import { useMemberGroups } from 'web/hooks/use-group'
-import { NEW_USER_GROUP_SLUGS } from 'common/group'
+import { Group, NEW_USER_GROUP_SLUGS } from 'common/group'
+import { PillButton } from './buttons/pill-button'
+import { sortBy } from 'lodash'
+import { DEFAULT_CATEGORY_GROUPS } from 'common/categories'
 
 const searchClient = algoliasearch(
   'GJQPAYENIF',
@@ -36,14 +39,16 @@ const indexPrefix = ENV === 'DEV' ? 'dev-' : ''
 
 const sortIndexes = [
   { label: 'Newest', value: indexPrefix + 'contracts-newest' },
-  { label: 'Oldest', value: indexPrefix + 'contracts-oldest' },
-  { label: 'Most popular', value: indexPrefix + 'contracts-most-popular' },
+  // { label: 'Oldest', value: indexPrefix + 'contracts-oldest' },
+  { label: 'Most popular', value: indexPrefix + 'contracts-score' },
   { label: 'Most traded', value: indexPrefix + 'contracts-most-traded' },
   { label: '24h volume', value: indexPrefix + 'contracts-24-hour-vol' },
   { label: 'Last updated', value: indexPrefix + 'contracts-last-updated' },
+  { label: 'Subsidy', value: indexPrefix + 'contracts-liquidity' },
   { label: 'Close date', value: indexPrefix + 'contracts-close-date' },
   { label: 'Resolve date', value: indexPrefix + 'contracts-resolve-date' },
 ]
+export const DEFAULT_SORT = 'score'
 
 type filter = 'personal' | 'open' | 'closed' | 'resolved' | 'all'
 
@@ -76,9 +81,24 @@ export function ContractSearch(props: {
   } = props
 
   const user = useUser()
-  const memberGroupSlugs = useMemberGroups(user?.id)
-    ?.map((g) => g.slug)
-    .filter((s) => !NEW_USER_GROUP_SLUGS.includes(s))
+  const memberGroups = (useMemberGroups(user?.id) ?? []).filter(
+    (group) => !NEW_USER_GROUP_SLUGS.includes(group.slug)
+  )
+  const memberGroupSlugs =
+    memberGroups.length > 0
+      ? memberGroups.map((g) => g.slug)
+      : DEFAULT_CATEGORY_GROUPS.map((g) => g.slug)
+
+  const memberPillGroups = sortBy(
+    memberGroups.filter((group) => group.contractIds.length > 0),
+    (group) => group.contractIds.length
+  ).reverse()
+
+  const defaultPillGroups = DEFAULT_CATEGORY_GROUPS as Group[]
+
+  const pillGroups =
+    memberPillGroups.length > 0 ? memberPillGroups : defaultPillGroups
+
   const follows = useFollows(user?.id)
   const { initialSort } = useInitialQueryAndSort(querySortOptions)
 
@@ -86,34 +106,51 @@ export function ContractSearch(props: {
     .map(({ value }) => value)
     .includes(`${indexPrefix}contracts-${initialSort ?? ''}`)
     ? initialSort
-    : querySortOptions?.defaultSort ?? 'most-popular'
+    : querySortOptions?.defaultSort ?? DEFAULT_SORT
 
   const [filter, setFilter] = useState<filter>(
     querySortOptions?.defaultFilter ?? 'open'
   )
+  const pillsEnabled = !additionalFilter
+
+  const [pillFilter, setPillFilter] = useState<string | undefined>(undefined)
+
+  const selectFilter = (pill: string | undefined) => () => {
+    setPillFilter(pill)
+    track('select search category', { category: pill ?? 'all' })
+  }
 
   const { filters, numericFilters } = useMemo(() => {
     let filters = [
       filter === 'open' ? 'isResolved:false' : '',
       filter === 'closed' ? 'isResolved:false' : '',
       filter === 'resolved' ? 'isResolved:true' : '',
-      filter === 'personal'
+      additionalFilter?.creatorId
+        ? `creatorId:${additionalFilter.creatorId}`
+        : '',
+      additionalFilter?.tag ? `lowercaseTags:${additionalFilter.tag}` : '',
+      additionalFilter?.groupSlug
+        ? `groupLinks.slug:${additionalFilter.groupSlug}`
+        : '',
+      pillFilter && pillFilter !== 'personal' && pillFilter !== 'your-bets'
+        ? `groupLinks.slug:${pillFilter}`
+        : '',
+      pillFilter === 'personal'
         ? // Show contracts in groups that the user is a member of
-          (memberGroupSlugs?.map((slug) => `groupSlugs:${slug}`) ?? [])
+          memberGroupSlugs
+            .map((slug) => `groupLinks.slug:${slug}`)
             // Show contracts created by users the user follows
             .concat(follows?.map((followId) => `creatorId:${followId}`) ?? [])
             // Show contracts bet on by users the user follows
             .concat(
               follows?.map((followId) => `uniqueBettorIds:${followId}`) ?? []
-              // Show contracts bet on by the user
             )
-            .concat(user ? `uniqueBettorIds:${user.id}` : [])
         : '',
-      additionalFilter?.creatorId
-        ? `creatorId:${additionalFilter.creatorId}`
-        : '',
-      additionalFilter?.groupSlug
-        ? `groupSlugs:${additionalFilter.groupSlug}`
+      // Subtract contracts you bet on from For you.
+      pillFilter === 'personal' && user ? `uniqueBettorIds:-${user.id}` : '',
+      pillFilter === 'your-bets' && user
+        ? // Show contracts bet on by the user
+          `uniqueBettorIds:${user.id}`
         : '',
     ].filter((f) => f)
     // Hack to make Algolia work.
@@ -128,8 +165,9 @@ export function ContractSearch(props: {
   }, [
     filter,
     Object.values(additionalFilter ?? {}).join(','),
-    (memberGroupSlugs ?? []).join(','),
+    memberGroupSlugs.join(','),
     (follows ?? []).join(','),
+    pillFilter,
   ])
 
   const indexName = `${indexPrefix}contracts-${sort}`
@@ -160,12 +198,11 @@ export function ContractSearch(props: {
           className="!select !select-bordered"
           value={filter}
           onChange={(e) => setFilter(e.target.value as filter)}
-          onBlur={trackCallback('select search filter')}
+          onBlur={trackCallback('select search filter', { filter })}
         >
           <option value="open">Open</option>
           <option value="closed">Closed</option>
           <option value="resolved">Resolved</option>
-          <option value="personal">For you</option>
           <option value="all">All</option>
         </select>
         {!hideOrderSelector && (
@@ -174,7 +211,7 @@ export function ContractSearch(props: {
             classNames={{
               select: '!select !select-bordered',
             }}
-            onBlur={trackCallback('select search sort')}
+            onBlur={trackCallback('select search sort', { sort })}
           />
         )}
         <Configure
@@ -187,11 +224,52 @@ export function ContractSearch(props: {
 
       <Spacer h={3} />
 
-      {/*<Spacer h={4} />*/}
+      {pillsEnabled && (
+        <Row className="scrollbar-hide items-start gap-2 overflow-x-auto">
+          <PillButton
+            key={'all'}
+            selected={pillFilter === undefined}
+            onSelect={selectFilter(undefined)}
+          >
+            All
+          </PillButton>
+          <PillButton
+            key={'personal'}
+            selected={pillFilter === 'personal'}
+            onSelect={selectFilter('personal')}
+          >
+            {user ? 'For you' : 'Featured'}
+          </PillButton>
+
+          {user && (
+            <PillButton
+              key={'your-bets'}
+              selected={pillFilter === 'your-bets'}
+              onSelect={selectFilter('your-bets')}
+            >
+              Your bets
+            </PillButton>
+          )}
+
+          {pillGroups.map(({ name, slug }) => {
+            return (
+              <PillButton
+                key={slug}
+                selected={pillFilter === slug}
+                onSelect={selectFilter(slug)}
+              >
+                {name}
+              </PillButton>
+            )
+          })}
+        </Row>
+      )}
+
+      <Spacer h={3} />
 
       {filter === 'personal' &&
       (follows ?? []).length === 0 &&
-      (memberGroupSlugs ?? []).length === 0 ? (
+      memberGroupSlugs.length === 0 ? (
         <>You're not following anyone, nor in any of your own groups yet.</>
       ) : (
         <ContractSearchInner
diff --git a/web/components/contract/contract-description.tsx b/web/components/contract/contract-description.tsx
index b2f839e9..f9db0cd9 100644
--- a/web/components/contract/contract-description.tsx
+++ b/web/components/contract/contract-description.tsx
@@ -2,16 +2,17 @@ import clsx from 'clsx'
 import dayjs from 'dayjs'
 import { useState } from 'react'
 import Textarea from 'react-expanding-textarea'
-import { CATEGORY_LIST } from '../../../common/categories'
 
-import { Contract } from 'common/contract'
-import { parseTags, exhibitExts } from 'common/util/parse'
+import { Contract, MAX_DESCRIPTION_LENGTH } from 'common/contract'
+import { exhibitExts, parseTags } from 'common/util/parse'
 import { useAdmin } from 'web/hooks/use-admin'
 import { updateContract } from 'web/lib/firebase/contracts'
 import { Row } from '../layout/row'
-import { TagsList } from '../tags-list'
 import { Content } from '../editor'
-import { Editor } from '@tiptap/react'
+import { TextEditor, useTextEditor } from 'web/components/editor'
+import { Button } from '../button'
+import { Spacer } from '../layout/spacer'
+import { Editor, Content as ContentType } from '@tiptap/react'
 
 export function ContractDescription(props: {
   contract: Contract
@@ -19,20 +20,36 @@ export function ContractDescription(props: {
   className?: string
 }) {
   const { contract, isCreator, className } = props
-  const descriptionTimestamp = () => `${dayjs().format('MMM D, h:mma')}: `
   const isAdmin = useAdmin()
+  return (
+    <div className={clsx('mt-2 text-gray-700', className)}>
+      {isCreator || isAdmin ? (
+        <RichEditContract contract={contract} isAdmin={isAdmin && !isCreator} />
+      ) : (
+        <Content content={contract.description} />
+      )}
+    </div>
+  )
+}
 
-  const desc = contract.description ?? ''
+function editTimestamp() {
+  return `${dayjs().format('MMM D, h:mma')}: `
+}
 
-  // Append the new description (after a newline)
-  async function saveDescription(newText: string) {
-    const editor = new Editor({ content: desc, extensions: exhibitExts })
-    editor
-      .chain()
-      .focus('end')
-      .insertContent('<br /><br />')
-      .insertContent(newText.trim())
-      .run()
+function RichEditContract(props: { contract: Contract; isAdmin?: boolean }) {
+  const { contract, isAdmin } = props
+  const [editing, setEditing] = useState(false)
+  const [editingQ, setEditingQ] = useState(false)
+  const [isSubmitting, setIsSubmitting] = useState(false)
+
+  const { editor, upload } = useTextEditor({
+    max: MAX_DESCRIPTION_LENGTH,
+    defaultValue: contract.description,
+    disabled: isSubmitting,
+  })
+
+  async function saveDescription() {
+    if (!editor) return
 
     const tags = parseTags(
       `${editor.getText()} ${contract.tags.map((tag) => `#${tag}`).join(' ')}`
@@ -46,76 +63,94 @@ export function ContractDescription(props: {
     })
   }
 
-  const { tags } = contract
-  const categories = tags.filter((tag) =>
-    CATEGORY_LIST.includes(tag.toLowerCase())
-  )
-
-  return (
-    <div
-      className={clsx(
-        'mt-2 whitespace-pre-line break-words text-gray-700',
-        className
-      )}
-    >
-      <Content content={desc} />
-
-      {categories.length > 0 && (
-        <div className="mt-4">
-          <TagsList tags={categories} noLabel />
-        </div>
-      )}
-
-      <br />
-
-      {isCreator && (
-        <EditContract
-          // Note: Because descriptionTimestamp is called once, later edits use
-          // a stale timestamp. Ideally this is a function that gets called when
-          // isEditing is set to true.
-          text={descriptionTimestamp()}
-          onSave={saveDescription}
-          buttonText="Add to description"
-        />
-      )}
-      {isAdmin && (
-        <EditContract
-          text={contract.question}
-          onSave={(question) => updateContract(contract.id, { question })}
-          buttonText="ADMIN: Edit question"
-        />
-      )}
-      {/* {isAdmin && (
-        <EditContract
-          text={contract.createdTime.toString()}
-          onSave={(time) =>
-            updateContract(contract.id, { createdTime: Number(time) })
-          }
-          buttonText="ADMIN: Edit createdTime"
-        />
-      )} */}
-    </div>
+  return editing ? (
+    <>
+      <TextEditor editor={editor} upload={upload} />
+      <Spacer h={2} />
+      <Row className="gap-2">
+        <Button
+          onClick={async () => {
+            setIsSubmitting(true)
+            await saveDescription()
+            setEditing(false)
+            setIsSubmitting(false)
+          }}
+        >
+          Save
+        </Button>
+        <Button color="gray" onClick={() => setEditing(false)}>
+          Cancel
+        </Button>
+      </Row>
+    </>
+  ) : (
+    <>
+      <Content content={contract.description} />
+      <Spacer h={2} />
+      <Row className="items-center gap-2">
+        {isAdmin && 'Admin: '}
+        <Button
+          color="gray"
+          size="xs"
+          onClick={() => {
+            setEditing(true)
+            editor
+              ?.chain()
+              .setContent(contract.description)
+              .focus('end')
+              .insertContent(`<p>${editTimestamp()}</p>`)
+              .run()
+          }}
+        >
+          Edit description
+        </Button>
+        <Button color="gray" size="xs" onClick={() => setEditingQ(true)}>
+          Edit question
+        </Button>
+      </Row>
+      <EditQuestion
+        contract={contract}
+        editing={editingQ}
+        setEditing={setEditingQ}
+      />
+    </>
   )
 }
 
-function EditContract(props: {
-  text: string
-  onSave: (newText: string) => void
-  buttonText: string
+function EditQuestion(props: {
+  contract: Contract
+  editing: boolean
+  setEditing: (editing: boolean) => void
 }) {
-  const [text, setText] = useState(props.text)
-  const [editing, setEditing] = useState(false)
-  const onSave = (newText: string) => {
+  const { contract, editing, setEditing } = props
+  const [text, setText] = useState(contract.question)
+
+  function questionChanged(oldQ: string, newQ: string) {
+    return `<p>${editTimestamp()}<s>${oldQ}</s> → ${newQ}</p>`
+  }
+
+  function joinContent(oldContent: ContentType, newContent: string) {
+    const editor = new Editor({ content: oldContent, extensions: exhibitExts })
+    editor.chain().focus('end').insertContent(newContent).run()
+    return editor.getJSON()
+  }
+
+  const onSave = async (newText: string) => {
     setEditing(false)
-    setText(props.text) // Reset to original text
-    props.onSave(newText)
+    await updateContract(contract.id, {
+      question: newText,
+      description: joinContent(
+        contract.description,
+        questionChanged(contract.question, newText)
+      ),
+    })
   }
 
   return editing ? (
     <div className="mt-4">
       <Textarea
         className="textarea textarea-bordered mb-1 h-24 w-full resize-none"
-        rows={3}
+        rows={2}
         value={text}
         onChange={(e) => setText(e.target.value || '')}
         autoFocus
@@ -130,28 +165,11 @@ function EditContract(props: {
         }}
       />
       <Row className="gap-2">
-        <button
-          className="btn btn-neutral btn-outline btn-sm"
-          onClick={() => onSave(text)}
-        >
-          Save
-        </button>
-        <button
-          className="btn btn-error btn-outline btn-sm"
-          onClick={() => setEditing(false)}
-        >
+        <Button onClick={() => onSave(text)}>Save</Button>
+        <Button color="gray" onClick={() => setEditing(false)}>
           Cancel
-        </button>
+        </Button>
       </Row>
     </div>
-  ) : (
-    <Row>
-      <button
-        className="btn btn-neutral btn-outline btn-xs mt-4"
-        onClick={() => setEditing(true)}
-      >
-        {props.buttonText}
-      </button>
-    </Row>
-  )
+  ) : null
 }
diff --git a/web/components/contract/contract-details.tsx b/web/components/contract/contract-details.tsx
index b4d67520..83c291c7 100644
--- a/web/components/contract/contract-details.tsx
+++ b/web/components/contract/contract-details.tsx
@@ -11,7 +11,7 @@ import { UserLink } from '../user-page'
 import {
   Contract,
   contractMetrics,
-  contractPool,
+  contractPath,
   updateContract,
 } from 'web/lib/firebase/contracts'
 import dayjs from 'dayjs'
@@ -22,17 +22,19 @@ import { useState } from 'react'
 import { ContractInfoDialog } from './contract-info-dialog'
 import { Bet } from 'common/bet'
 import NewContractBadge from '../new-contract-badge'
-import { CATEGORY_LIST } from 'common/categories'
-import { TagsList } from '../tags-list'
 import { UserFollowButton } from '../follow-button'
-import { groupPath } from 'web/lib/firebase/groups'
-import { SiteLink } from 'web/components/site-link'
 import { DAY_MS } from 'common/util/time'
-import { useGroupsWithContract } from 'web/hooks/use-group'
 import { ShareIconButton } from 'web/components/share-icon-button'
 import { useUser } from 'web/hooks/use-user'
 import { Editor } from '@tiptap/react'
 import { exhibitExts } from 'common/util/parse'
+import { ENV_CONFIG } from 'common/envs/constants'
+import { Button } from 'web/components/button'
+import { Modal } from 'web/components/layout/modal'
+import { Col } from 'web/components/layout/col'
+import { ContractGroupsList } from 'web/components/groups/contract-groups-list'
+import { SiteLink } from 'web/components/site-link'
+import { groupPath } from 'web/lib/firebase/groups'
 
 export type ShowTime = 'resolve-date' | 'close-date'
 
@@ -46,15 +48,12 @@ export function MiscDetails(props: {
     volume,
     volume24Hours,
     closeTime,
-    tags,
     isResolved,
     createdTime,
     resolutionTime,
+    groupLinks,
   } = contract
-  // Show at most one category that this contract is tagged by
-  const categories = CATEGORY_LIST.filter((category) =>
-    tags.map((t) => t.toLowerCase()).includes(category)
-  ).slice(0, 1)
+
   const isNew = createdTime > Date.now() - DAY_MS && !isResolved
 
   return (
@@ -76,13 +75,21 @@ export function MiscDetails(props: {
           {fromNow(resolutionTime || 0)}
         </Row>
       ) : volume > 0 || !isNew ? (
-        <Row>{contractPool(contract)} pool</Row>
+        <Row className={'shrink-0'}>{formatMoney(contract.volume)} bet</Row>
       ) : (
         <NewContractBadge />
       )}
 
-      {categories.length > 0 && (
-        <TagsList className="text-gray-400" tags={categories} noLabel />
+      {groupLinks && groupLinks.length > 0 && (
+        <SiteLink
+          href={groupPath(groupLinks[0].slug)}
+          className="text-sm text-gray-400"
+        >
+          <Row className={'line-clamp-1 flex-wrap items-center '}>
+            <UserGroupIcon className="mx-1 mb-0.5 inline h-4 w-4 shrink-0" />
+            {groupLinks[0].name}
+          </Row>
+        </SiteLink>
       )}
     </Row>
   )
@@ -130,34 +137,15 @@ export function ContractDetails(props: {
   disabled?: boolean
 }) {
   const { contract, bets, isCreator, disabled } = props
-  const { closeTime, creatorName, creatorUsername, creatorId } = contract
+  const { closeTime, creatorName, creatorUsername, creatorId, groupLinks } =
+    contract
   const { volumeLabel, resolvedDate } = contractMetrics(contract)
 
-  const groups = (useGroupsWithContract(contract.id) ?? []).sort((g1, g2) => {
-    return g2.createdTime - g1.createdTime
-  })
-  const user = useUser()
-
-  const groupsUserIsMemberOf = groups
-    ? groups.filter((g) => g.memberIds.includes(contract.creatorId))
-    : []
-  const groupsUserIsCreatorOf = groups
-    ? groups.filter((g) => g.creatorId === contract.creatorId)
-    : []
-
-  // Priorities for which group the contract belongs to:
-  // In order of created most recently
-  // Group that the contract owner created
-  // Group the contract owner is a member of
-  // Any group the contract is in
   const groupToDisplay =
-    groupsUserIsCreatorOf.length > 0
-      ? groupsUserIsCreatorOf[0]
-      : groupsUserIsMemberOf.length > 0
-      ? groupsUserIsMemberOf[0]
-      : groups
-      ? groups[0]
-      : undefined
+    groupLinks?.sort((a, b) => a.createdTime - b.createdTime)[0] ?? null
+  const user = useUser()
+  const [open, setOpen] = useState(false)
+
   return (
     <Row className="flex-1 flex-wrap items-center gap-x-4 gap-y-2 text-sm text-gray-500">
       <Row className="items-center gap-2">
@@ -178,16 +166,34 @@ export function ContractDetails(props: {
         )}
         {!disabled && <UserFollowButton userId={creatorId} small />}
       </Row>
-      {groupToDisplay ? (
-        <Row className={'line-clamp-1 mt-1 max-w-[200px]'}>
-          <SiteLink href={`${groupPath(groupToDisplay.slug)}`}>
-            <UserGroupIcon className="mx-1 mb-1 inline h-5 w-5" />
-            <span>{groupToDisplay.name}</span>
-          </SiteLink>
-        </Row>
-      ) : (
-        <div />
-      )}
+      <Row>
+        <Button
+          size={'xs'}
+          className={'max-w-[200px]'}
+          color={'gray-white'}
+          onClick={() => setOpen(!open)}
+        >
+          <Row>
+            <UserGroupIcon className="mx-1 inline h-5 w-5 shrink-0" />
+            <span className={'line-clamp-1'}>
+              {groupToDisplay ? groupToDisplay.name : 'No group'}
+            </span>
+          </Row>
+        </Button>
+      </Row>
+      <Modal open={open} setOpen={setOpen} size={'md'}>
+        <Col
+          className={
+            'max-h-[70vh] min-h-[20rem] overflow-auto rounded bg-white p-6'
+          }
+        >
+          <ContractGroupsList
+            groupLinks={groupLinks ?? []}
+            contract={contract}
+            user={user}
+          />
+        </Col>
+      </Modal>
 
       {(!!closeTime || !!resolvedDate) && (
         <Row className="items-center gap-1">
@@ -222,9 +228,12 @@ export function ContractDetails(props: {
         <div className="whitespace-nowrap">{volumeLabel}</div>
       </Row>
       <ShareIconButton
-        contract={contract}
+        copyPayload={`https://${ENV_CONFIG.domain}${contractPath(contract)}${
+          user?.username && contract.creatorUsername !== user?.username
+            ? '?referrer=' + user?.username
+            : ''
+        }`}
         toastClassName={'sm:-left-40 -left-24 min-w-[250%]'}
-        username={user?.username}
       />
 
       {!disabled && <ContractInfoDialog contract={contract} bets={bets} />}
@@ -321,12 +330,13 @@ function EditableCloseDate(props: {
             Done
           </button>
         ) : (
-          <button
-            className="btn btn-xs btn-ghost"
+          <Button
+            size={'xs'}
+            color={'gray-white'}
             onClick={() => setIsEditingCloseTime(true)}
           >
-            <PencilIcon className="mr-2 inline h-4 w-4" /> Edit
-          </button>
+            <PencilIcon className="mr-0.5 inline h-4 w-4" /> Edit
+          </Button>
         ))}
     </>
   )
diff --git a/web/components/contract/contract-info-dialog.tsx b/web/components/contract/contract-info-dialog.tsx
index b5ecea15..d976253f 100644
--- a/web/components/contract/contract-info-dialog.tsx
+++ b/web/components/contract/contract-info-dialog.tsx
@@ -16,11 +16,10 @@ import { ShareEmbedButton } from '../share-embed-button'
 import { Title } from '../title'
 import { TweetButton } from '../tweet-button'
 import { InfoTooltip } from '../info-tooltip'
-import { TagsInput } from 'web/components/tags-input'
 import { DuplicateContractButton } from '../copy-contract-button'
 
 export const contractDetailsButtonClassName =
-  'group flex items-center rounded-md px-3 py-2 text-sm font-medium  cursor-pointer hover:bg-gray-100 text-gray-400 hover:text-gray-500'
+  'group flex items-center rounded-md px-3 py-2 text-sm font-medium cursor-pointer hover:bg-gray-100 text-gray-400 hover:text-gray-500'
 
 export function ContractInfoDialog(props: { contract: Contract; bets: Bet[] }) {
   const { contract, bets } = props
@@ -141,9 +140,6 @@ export function ContractInfoDialog(props: { contract: Contract; bets: Bet[] }) {
             </tbody>
           </table>
 
-          <div>Tags</div>
-          <TagsInput contract={contract} />
-          <div />
           {contract.mechanism === 'cpmm-1' && !contract.resolution && (
             <LiquidityPanel contract={contract} />
           )}
diff --git a/web/components/contract/contract-leaderboard.tsx b/web/components/contract/contract-leaderboard.tsx
new file mode 100644
index 00000000..deb9b857
--- /dev/null
+++ b/web/components/contract/contract-leaderboard.tsx
@@ -0,0 +1,141 @@
+import { Bet } from 'common/bet'
+import { Comment } from 'common/comment'
+import { resolvedPayout } from 'common/calculate'
+import { Contract } from 'common/contract'
+import { formatMoney } from 'common/util/format'
+import { groupBy, mapValues, sumBy, sortBy, keyBy } from 'lodash'
+import { useState, useMemo, useEffect } from 'react'
+import { CommentTipMap } from 'web/hooks/use-tip-txns'
+import { useUserById } from 'web/hooks/use-user'
+import { listUsers, User } from 'web/lib/firebase/users'
+import { FeedBet } from '../feed/feed-bets'
+import { FeedComment } from '../feed/feed-comments'
+import { Spacer } from '../layout/spacer'
+import { Leaderboard } from '../leaderboard'
+import { Title } from '../title'
+
+export function ContractLeaderboard(props: {
+  contract: Contract
+  bets: Bet[]
+}) {
+  const { contract, bets } = props
+  const [users, setUsers] = useState<User[]>()
+
+  const { userProfits, top5Ids } = useMemo(() => {
+    // Create a map of userIds to total profits (including sales)
+    const openBets = bets.filter((bet) => !bet.isSold && !bet.sale)
+    const betsByUser = groupBy(openBets, 'userId')
+
+    const userProfits = mapValues(betsByUser, (bets) =>
+      sumBy(bets, (bet) => resolvedPayout(contract, bet) - bet.amount)
+    )
+    // Find the 5 users with the most profits
+    const top5Ids = Object.entries(userProfits)
+      .sort(([_i1, p1], [_i2, p2]) => p2 - p1)
+      .filter(([, p]) => p > 0)
+      .slice(0, 5)
+      .map(([id]) => id)
+    return { userProfits, top5Ids }
+  }, [contract, bets])
+
+  useEffect(() => {
+    if (top5Ids.length > 0) {
+      listUsers(top5Ids).then((users) => {
+        const sortedUsers = sortBy(users, (user) => -userProfits[user.id])
+        setUsers(sortedUsers)
+      })
+    }
+  }, [userProfits, top5Ids])
+
+  return users && users.length > 0 ? (
+    <Leaderboard
+      title="🏅 Top traders"
+      users={users || []}
+      columns={[
+        {
+          header: 'Total profit',
+          renderCell: (user) => formatMoney(userProfits[user.id] || 0),
+        },
+      ]}
+      className="mt-12 max-w-sm"
+    />
+  ) : null
+}
+
+export function ContractTopTrades(props: {
+  contract: Contract
+  bets: Bet[]
+  comments: Comment[]
+  tips: CommentTipMap
+}) {
+  const { contract, bets, comments, tips } = props
+  const commentsById = keyBy(comments, 'id')
+  const betsById = keyBy(bets, 'id')
+
+  // If 'id2' is the sale of 'id1', both are logged with (id2 - id1) of profit
+  // Otherwise, we record the profit at resolution time
+  const profitById: Record<string, number> = {}
+  for (const bet of bets) {
+    if (bet.sale) {
+      const originalBet = betsById[bet.sale.betId]
+      const profit = bet.sale.amount - originalBet.amount
+      profitById[bet.id] = profit
+      profitById[originalBet.id] = profit
+    } else {
+      profitById[bet.id] = resolvedPayout(contract, bet) - bet.amount
+    }
+  }
+
+  // Now find the betId with the highest profit
+  const topBetId = sortBy(bets, (b) => -profitById[b.id])[0]?.id
+  const topBettor = useUserById(betsById[topBetId]?.userId)
+
+  // And also the commentId of the comment with the highest profit
+  const topCommentId = sortBy(
+    comments,
+    (c) => c.betId && -profitById[c.betId]
+  )[0]?.id
+
+  return (
+    <div className="mt-12 max-w-sm">
+      {topCommentId && profitById[topCommentId] > 0 && (
+        <>
+          <Title text="💬 Proven correct" className="!mt-0" />
+          <div className="relative flex items-start space-x-3 rounded-md bg-gray-50 px-2 py-4">
+            <FeedComment
+              contract={contract}
+              comment={commentsById[topCommentId]}
+              tips={tips[topCommentId]}
+              betsBySameUser={[betsById[topCommentId]]}
+              truncate={false}
+              smallAvatar={false}
+            />
+          </div>
+          <div className="mt-2 text-sm text-gray-500">
+            {commentsById[topCommentId].userName} made{' '}
+            {formatMoney(profitById[topCommentId] || 0)}!
+          </div>
+          <Spacer h={16} />
+        </>
+      )}
+
+      {/* If they're the same, only show the comment; otherwise show both */}
+      {topBettor && topBetId !== topCommentId && profitById[topBetId] > 0 && (
+        <>
+          <Title text="💸 Smartest money" className="!mt-0" />
+          <div className="relative flex items-start space-x-3 rounded-md bg-gray-50 px-2 py-4">
+            <FeedBet
+              contract={contract}
+              bet={betsById[topBetId]}
+              hideOutcome={false}
+              smallAvatar={false}
+            />
+          </div>
+          <div className="mt-2 text-sm text-gray-500">
+            {topBettor?.name} made {formatMoney(profitById[topBetId] || 0)}!
+          </div>
+        </>
+      )}
+    </div>
+  )
+}
diff --git a/web/components/contract/contract-prob-graph.tsx b/web/components/contract/contract-prob-graph.tsx
index a9d26e2e..98440ec8 100644
--- a/web/components/contract/contract-prob-graph.tsx
+++ b/web/components/contract/contract-prob-graph.tsx
@@ -7,7 +7,6 @@ import { Bet } from 'common/bet'
 import { getInitialProbability } from 'common/calculate'
 import { BinaryContract, PseudoNumericContract } from 'common/contract'
 import { useWindowSize } from 'web/hooks/use-window-size'
-import { getMappedValue } from 'common/pseudo-numeric'
 import { formatLargeNumber } from 'common/util/format'
 
 export const ContractProbGraph = memo(function ContractProbGraph(props: {
@@ -29,7 +28,11 @@ export const ContractProbGraph = memo(function ContractProbGraph(props: {
     ...bets.map((bet) => bet.createdTime),
   ].map((time) => new Date(time))
 
-  const f = getMappedValue(contract)
+  const f: (p: number) => number = isBinary
+    ? (p) => p
+    : isLogScale
+    ? (p) => p * Math.log10(contract.max - contract.min + 1)
+    : (p) => p * (contract.max - contract.min) + contract.min
 
   const probs = [startProb, ...bets.map((bet) => bet.probAfter)].map(f)
 
@@ -69,10 +72,9 @@ export const ContractProbGraph = memo(function ContractProbGraph(props: {
 
   const points: { x: Date; y: number }[] = []
   const s = isBinary ? 100 : 1
-  const c = isLogScale && contract.min === 0 ? 1 : 0
 
   for (let i = 0; i < times.length - 1; i++) {
-    points[points.length] = { x: times[i], y: s * probs[i] + c }
+    points[points.length] = { x: times[i], y: s * probs[i] }
     const numPoints: number = Math.floor(
       dayjs(times[i + 1]).diff(dayjs(times[i]), 'ms') / timeStep
     )
@@ -84,7 +86,7 @@ export const ContractProbGraph = memo(function ContractProbGraph(props: {
           x: dayjs(times[i])
             .add(thisTimeStep * n, 'ms')
             .toDate(),
-          y: s * probs[i] + c,
+          y: s * probs[i],
         }
       }
     }
@@ -99,6 +101,9 @@ export const ContractProbGraph = memo(function ContractProbGraph(props: {
 
   const formatter = isBinary
     ? formatPercent
+    : isLogScale
+    ? (x: DatumValue) =>
+        formatLargeNumber(10 ** +x.valueOf() + contract.min - 1)
     : (x: DatumValue) => formatLargeNumber(+x.valueOf())
 
   return (
@@ -111,11 +116,13 @@ export const ContractProbGraph = memo(function ContractProbGraph(props: {
         yScale={
           isBinary
             ? { min: 0, max: 100, type: 'linear' }
-            : {
-                min: contract.min + c,
-                max: contract.max + c,
-                type: contract.isLogScale ? 'log' : 'linear',
+            : isLogScale
+            ? {
+                min: 0,
+                max: Math.log10(contract.max - contract.min + 1),
+                type: 'linear',
               }
+            : { min: contract.min, max: contract.max, type: 'linear' }
         }
         yFormat={formatter}
         gridYValues={yTickValues}
@@ -143,6 +150,7 @@ export const ContractProbGraph = memo(function ContractProbGraph(props: {
         enableSlices="x"
         enableGridX={!!width && width >= 800}
         enableArea
+        areaBaselineValue={isBinary || isLogScale ? 0 : contract.min}
         margin={{ top: 20, right: 20, bottom: 25, left: 40 }}
         animate={false}
         sliceTooltip={SliceTooltip}
diff --git a/web/components/copy-link-button.tsx b/web/components/copy-link-button.tsx
index ab6dd66f..4ce4140d 100644
--- a/web/components/copy-link-button.tsx
+++ b/web/components/copy-link-button.tsx
@@ -3,58 +3,63 @@ import { LinkIcon } from '@heroicons/react/outline'
 import { Menu, Transition } from '@headlessui/react'
 import clsx from 'clsx'
 
-import { Contract } from 'common/contract'
 import { copyToClipboard } from 'web/lib/util/copy'
-import { contractPath } from 'web/lib/firebase/contracts'
-import { ENV_CONFIG } from 'common/envs/constants'
 import { ToastClipboard } from 'web/components/toast-clipboard'
 import { track } from 'web/lib/service/analytics'
-
-function copyContractUrl(contract: Contract) {
-  copyToClipboard(`https://${ENV_CONFIG.domain}${contractPath(contract)}`)
-}
+import { Row } from './layout/row'
 
 export function CopyLinkButton(props: {
-  contract: Contract
+  url: string
+  displayUrl?: string
+  tracking?: string
   buttonClassName?: string
   toastClassName?: string
 }) {
-  const { contract, buttonClassName, toastClassName } = props
+  const { url, displayUrl, tracking, buttonClassName, toastClassName } = props
 
   return (
-    <Menu
-      as="div"
-      className="relative z-10 flex-shrink-0"
-      onMouseUp={() => {
-        copyContractUrl(contract)
-        track('copy share link')
-      }}
-    >
-      <Menu.Button
-        className={clsx(
-          'btn btn-xs border-2 border-green-600 bg-white normal-case text-green-600 hover:border-green-600 hover:bg-white',
-          buttonClassName
-        )}
-      >
-        <LinkIcon className="mr-1.5 h-4 w-4" aria-hidden="true" />
-        Copy link
-      </Menu.Button>
+    <Row className="w-full">
+      <input
+        className="input input-bordered flex-1 rounded-r-none text-gray-500"
+        readOnly
+        type="text"
+        value={displayUrl ?? url}
+      />
 
-      <Transition
-        as={Fragment}
-        enter="transition ease-out duration-100"
-        enterFrom="transform opacity-0 scale-95"
-        enterTo="transform opacity-100 scale-100"
-        leave="transition ease-in duration-75"
-        leaveFrom="transform opacity-100 scale-100"
-        leaveTo="transform opacity-0 scale-95"
+      <Menu
+        as="div"
+        className="relative z-10 flex-shrink-0"
+        onMouseUp={() => {
+          copyToClipboard(url)
+          track(tracking ?? 'copy share link')
+        }}
       >
-        <Menu.Items>
-          <Menu.Item>
-            <ToastClipboard className={toastClassName} />
-          </Menu.Item>
-        </Menu.Items>
-      </Transition>
-    </Menu>
+        <Menu.Button
+          className={clsx(
+            'btn btn-xs border-2 border-green-600 bg-white normal-case text-green-600 hover:border-green-600 hover:bg-white',
+            buttonClassName
+          )}
+        >
+          <LinkIcon className="mr-1.5 h-4 w-4" aria-hidden="true" />
+          Copy link
+        </Menu.Button>
+
+        <Transition
+          as={Fragment}
+          enter="transition ease-out duration-100"
+          enterFrom="transform opacity-0 scale-95"
+          enterTo="transform opacity-100 scale-100"
+          leave="transition ease-in duration-75"
+          leaveFrom="transform opacity-100 scale-100"
+          leaveTo="transform opacity-0 scale-95"
+        >
+          <Menu.Items>
+            <Menu.Item>
+              <ToastClipboard className={toastClassName} />
+            </Menu.Item>
+          </Menu.Items>
+        </Transition>
+      </Menu>
+    </Row>
   )
 }
diff --git a/web/components/create-question-button.tsx b/web/components/create-question-button.tsx
index a9161ac6..1b8ac11e 100644
--- a/web/components/create-question-button.tsx
+++ b/web/components/create-question-button.tsx
@@ -1,10 +1,12 @@
 import Link from 'next/link'
+import { useRouter } from 'next/router'
 import clsx from 'clsx'
 import { firebaseLogin, User } from 'web/lib/firebase/users'
 import React from 'react'
 
 export const createButtonStyle =
   'border-w-0 mx-auto mt-4 -ml-1 w-full rounded-md bg-gradient-to-r py-2.5 text-base font-semibold text-white shadow-sm lg:-ml-0 h-11'
+
 export const CreateQuestionButton = (props: {
   user: User | null | undefined
   overrideText?: string
@@ -15,17 +17,23 @@ export const CreateQuestionButton = (props: {
     'from-indigo-500 to-blue-500 hover:from-indigo-700 hover:to-blue-700'
 
   const { user, overrideText, className, query } = props
+  const router = useRouter()
   return (
     <div className={clsx('flex justify-center', className)}>
       {user ? (
         <Link href={`/create${query ? query : ''}`} passHref>
           <button className={clsx(gradient, createButtonStyle)}>
-            {overrideText ? overrideText : 'Create a question'}
+            {overrideText ? overrideText : 'Create a market'}
           </button>
         </Link>
       ) : (
         <button
-          onClick={firebaseLogin}
+          onClick={async () => {
+            // login, and then reload the page, to hit any SSR redirect (e.g.
+            // redirecting from / to /home for logged in users)
+            await firebaseLogin()
+            router.replace(router.asPath)
+          }}
           className={clsx(gradient, createButtonStyle)}
         >
           Sign in
diff --git a/web/components/editor.tsx b/web/components/editor.tsx
index 4b3e2cce..4dfddac9 100644
--- a/web/components/editor.tsx
+++ b/web/components/editor.tsx
@@ -12,16 +12,24 @@ import StarterKit from '@tiptap/starter-kit'
 import { Image } from '@tiptap/extension-image'
 import { Link } from '@tiptap/extension-link'
 import clsx from 'clsx'
-import { useEffect } from 'react'
+import { useEffect, useState } from 'react'
 import { Linkify } from './linkify'
 import { uploadImage } from 'web/lib/firebase/storage'
 import { useMutation } from 'react-query'
 import { exhibitExts } from 'common/util/parse'
 import { FileUploadButton } from './file-upload-button'
 import { linkClass } from './site-link'
+import Iframe from 'common/util/tiptap-iframe'
+import { CodeIcon, PhotographIcon } from '@heroicons/react/solid'
+import { Modal } from './layout/modal'
+import { Col } from './layout/col'
+import { Button } from './button'
+import { Row } from './layout/row'
+import { Spacer } from './layout/spacer'
 
 const proseClass = clsx(
-  'prose prose-sm prose-p:my-0 prose-li:my-0 prose-blockquote:not-italic max-w-none'
+  'prose prose-p:my-0 prose-li:my-0 prose-blockquote:not-italic max-w-none prose-quoteless leading-relaxed',
+  'font-light prose-a:font-light prose-blockquote:font-light'
 )
 
 export function useTextEditor(props: {
@@ -34,7 +42,7 @@ export function useTextEditor(props: {
 
   const editorClass = clsx(
     proseClass,
-    'box-content min-h-[6em] textarea textarea-bordered'
+    'min-h-[6em] resize-none outline-none border-none pt-3 px-4 focus:ring-0'
   )
 
   const editor = useEditor({
@@ -55,6 +63,7 @@ export function useTextEditor(props: {
           class: clsx('no-underline !text-indigo-700', linkClass),
         },
       }),
+      Iframe,
     ],
     content: defaultValue,
   })
@@ -68,12 +77,19 @@ export function useTextEditor(props: {
           (file) => file.type.startsWith('image')
         )
 
-        if (!imageFiles.length) {
-          return // if no files pasted, use default paste handler
+        if (imageFiles.length) {
+          event.preventDefault()
+          upload.mutate(imageFiles)
         }
 
-        event.preventDefault()
-        upload.mutate(imageFiles)
+        // If the pasted content is iframe code, directly inject it
+        const text = event.clipboardData?.getData('text/plain').trim() ?? ''
+        if (isValidIframe(text)) {
+          editor.chain().insertContent(text).run()
+          return true // Prevent the code from getting pasted as text
+        }
+
+        return // Otherwise, use default paste handler
       },
     },
   })
@@ -85,31 +101,76 @@ export function useTextEditor(props: {
   return { editor, upload }
 }
 
+function isValidIframe(text: string) {
+  return /^<iframe.*<\/iframe>$/.test(text)
+}
+
 export function TextEditor(props: {
   editor: Editor | null
   upload: ReturnType<typeof useUploadMutation>
 }) {
   const { editor, upload } = props
+  const [iframeOpen, setIframeOpen] = useState(false)
 
   return (
     <>
       {/* hide placeholder when focused */}
-      <div className="w-full [&:focus-within_p.is-empty]:before:content-none">
+      <div className="relative w-full [&:focus-within_p.is-empty]:before:content-none">
         {editor && (
           <FloatingMenu
             editor={editor}
-            className="w-full text-sm text-slate-300"
+            className={clsx(proseClass, '-ml-2 mr-2 w-full text-slate-300 ')}
           >
-            Type <em>*anything*</em> or even paste or{' '}
+            Type <em>*markdown*</em>. Paste or{' '}
             <FileUploadButton
               className="link text-blue-300"
               onFiles={upload.mutate}
             >
-              upload an image
-            </FileUploadButton>
+              upload
+            </FileUploadButton>{' '}
+            images!
           </FloatingMenu>
         )}
-        <EditorContent editor={editor} />
+        <div className="overflow-hidden rounded-lg border border-gray-300 shadow-sm focus-within:border-indigo-500 focus-within:ring-1 focus-within:ring-indigo-500">
+          <EditorContent editor={editor} />
+          {/* Spacer element to match the height of the toolbar */}
+          <div className="py-2" aria-hidden="true">
+            {/* Matches height of button in toolbar (1px border + 36px content height) */}
+            <div className="py-px">
+              <div className="h-9" />
+            </div>
+          </div>
+        </div>
+
+        {/* Toolbar, with buttons for image and embeds */}
+        <div className="absolute inset-x-0 bottom-0 flex justify-between py-2 pl-3 pr-2">
+          <div className="flex items-center space-x-5">
+            <div className="flex items-center">
+              <FileUploadButton
+                onFiles={upload.mutate}
+                className="-m-2.5 flex h-10 w-10 items-center justify-center rounded-full text-gray-400 hover:text-gray-500"
+              >
+                <PhotographIcon className="h-5 w-5" aria-hidden="true" />
+                <span className="sr-only">Upload an image</span>
+              </FileUploadButton>
+            </div>
+            <div className="flex items-center">
+              <button
+                type="button"
+                onClick={() => setIframeOpen(true)}
+                className="-m-2.5 flex h-10 w-10 items-center justify-center rounded-full text-gray-400 hover:text-gray-500"
+              >
+                <IframeModal
+                  editor={editor}
+                  open={iframeOpen}
+                  setOpen={setIframeOpen}
+                />
+                <CodeIcon className="h-5 w-5" aria-hidden="true" />
+                <span className="sr-only">Embed an iframe</span>
+              </button>
+            </div>
+          </div>
+        </div>
       </div>
       {upload.isLoading && <span className="text-xs">Uploading image...</span>}
       {upload.isError && (
@@ -119,9 +180,69 @@ export function TextEditor(props: {
   )
 }
 
+function IframeModal(props: {
+  editor: Editor | null
+  open: boolean
+  setOpen: (open: boolean) => void
+}) {
+  const { editor, open, setOpen } = props
+  const [embedCode, setEmbedCode] = useState('')
+  const valid = isValidIframe(embedCode)
+
+  return (
+    <Modal open={open} setOpen={setOpen}>
+      <Col className="gap-2 rounded bg-white p-6">
+        <label
+          htmlFor="embed"
+          className="block text-sm font-medium text-gray-700"
+        >
+          Embed a market, Youtube video, etc.
+        </label>
+        <input
+          type="text"
+          name="embed"
+          id="embed"
+          className="block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
+          placeholder='e.g. <iframe src="..."></iframe>'
+          value={embedCode}
+          onChange={(e) => setEmbedCode(e.target.value)}
+        />
+
+        {/* Preview the embed if it's valid */}
+        {valid ? <RichContent content={embedCode} /> : <Spacer h={2} />}
+
+        <Row className="gap-2">
+          <Button
+            disabled={!valid}
+            onClick={() => {
+              if (editor && valid) {
+                editor.chain().insertContent(embedCode).run()
+                setEmbedCode('')
+                setOpen(false)
+              }
+            }}
+          >
+            Embed
+          </Button>
+          <Button
+            color="gray"
+            onClick={() => {
+              setEmbedCode('')
+              setOpen(false)
+            }}
+          >
+            Cancel
+          </Button>
+        </Row>
+      </Col>
+    </Modal>
+  )
+}
+
 const useUploadMutation = (editor: Editor | null) =>
   useMutation(
     (files: File[]) =>
+      // TODO: Images should be uploaded under a particular username
       Promise.all(files.map((file) => uploadImage('default', file))),
     {
       onSuccess(urls) {
@@ -136,7 +257,7 @@ const useUploadMutation = (editor: Editor | null) =>
     }
   )
 
-function RichContent(props: { content: JSONContent }) {
+function RichContent(props: { content: JSONContent | string }) {
   const { content } = props
   const editor = useEditor({
     editorProps: { attributes: { class: proseClass } },
@@ -153,7 +274,9 @@ function RichContent(props: { content: JSONContent }) {
 export function Content(props: { content: JSONContent | string }) {
   const { content } = props
   return typeof content === 'string' ? (
-    <Linkify text={content} />
+    <div className="whitespace-pre-line font-light leading-relaxed">
+      <Linkify text={content} />
+    </div>
   ) : (
     <RichContent content={content} />
   )
diff --git a/web/components/feed/feed-answer-comment-group.tsx b/web/components/feed/feed-answer-comment-group.tsx
index 5c3be539..aabb1081 100644
--- a/web/components/feed/feed-answer-comment-group.tsx
+++ b/web/components/feed/feed-answer-comment-group.tsx
@@ -1,18 +1,13 @@
 import { Answer } from 'common/answer'
 import { Bet } from 'common/bet'
 import { Comment } from 'common/comment'
-import { formatPercent } from 'common/util/format'
 import React, { useEffect, useState } from 'react'
 import { Col } from 'web/components/layout/col'
-import { Modal } from 'web/components/layout/modal'
-import { AnswerBetPanel } from 'web/components/answers/answer-bet-panel'
 import { Row } from 'web/components/layout/row'
 import { Avatar } from 'web/components/avatar'
 import { UserLink } from 'web/components/user-page'
 import { Linkify } from 'web/components/linkify'
 import clsx from 'clsx'
-import { tradingAllowed } from 'web/lib/firebase/contracts'
-import { BuyButton } from 'web/components/yes-no-selector'
 import {
   CommentInput,
   CommentRepliesList,
@@ -23,7 +18,6 @@ import { useRouter } from 'next/router'
 import { groupBy } from 'lodash'
 import { User } from 'common/user'
 import { useEvent } from 'web/hooks/use-event'
-import { getDpmOutcomeProbability } from 'common/calculate-dpm'
 import { CommentTipMap } from 'web/hooks/use-tip-txns'
 
 export function FeedAnswerCommentGroup(props: {
@@ -38,7 +32,6 @@ export function FeedAnswerCommentGroup(props: {
   const { username, avatarUrl, name, text } = answer
 
   const [replyToUsername, setReplyToUsername] = useState('')
-  const [open, setOpen] = useState(false)
   const [showReply, setShowReply] = useState(false)
   const [inputRef, setInputRef] = useState<HTMLTextAreaElement | null>(null)
   const [highlighted, setHighlighted] = useState(false)
@@ -50,11 +43,6 @@ export function FeedAnswerCommentGroup(props: {
   const commentsList = comments.filter(
     (comment) => comment.answerOutcome === answer.number.toString()
   )
-  const thisAnswerProb = getDpmOutcomeProbability(
-    contract.totalShares,
-    answer.id
-  )
-  const probPercent = formatPercent(thisAnswerProb)
   const betsByCurrentUser = (user && betsByUserId[user.id]) ?? []
   const commentsByCurrentUser = (user && commentsByUserId[user.id]) ?? []
   const isFreeResponseContractPage = !!commentsByCurrentUser
@@ -112,27 +100,16 @@ export function FeedAnswerCommentGroup(props: {
   }, [answerElementId, router.asPath])
 
   return (
-    <Col className={'relative flex-1 gap-2'} key={answer.id + 'comment'}>
-      <Modal open={open} setOpen={setOpen}>
-        <AnswerBetPanel
-          answer={answer}
-          contract={contract}
-          closePanel={() => setOpen(false)}
-          className="sm:max-w-84 !rounded-md bg-white !px-8 !py-6"
-          isModal={true}
-        />
-      </Modal>
-
+    <Col className={'relative flex-1 gap-3'} key={answer.id + 'comment'}>
       <Row
         className={clsx(
-          'my-4 flex gap-3 space-x-3 transition-all duration-1000',
+          'flex gap-3 space-x-3 pt-4 transition-all duration-1000',
           highlighted ? `-m-2 my-3 rounded bg-indigo-500/[0.2] p-2` : ''
         )}
         id={answerElementId}
       >
-        <div className="px-1">
-          <Avatar username={username} avatarUrl={avatarUrl} />
-        </div>
+        <Avatar username={username} avatarUrl={avatarUrl} />
+
         <Col className="min-w-0 flex-1 lg:gap-1">
           <div className="text-sm text-gray-500">
             <UserLink username={username} name={name} /> answered
@@ -144,43 +121,21 @@ export function FeedAnswerCommentGroup(props: {
             />
           </div>
 
-          <Col className="align-items justify-between gap-4 sm:flex-row">
+          <Col className="align-items justify-between gap-2 sm:flex-row">
             <span className="whitespace-pre-line text-lg">
               <Linkify text={text} />
             </span>
 
-            <Row className="items-center justify-center gap-4">
-              {isFreeResponseContractPage && (
-                <div className={'sm:hidden'}>
-                  <button
-                    className={
-                      'text-xs font-bold text-gray-500 hover:underline'
-                    }
-                    onClick={() => scrollAndOpenReplyInput(undefined, answer)}
-                  >
-                    Reply
-                  </button>
-                </div>
-              )}
-
-              <div className={'align-items flex w-full justify-end gap-4 '}>
-                <span
-                  className={clsx(
-                    'text-2xl',
-                    tradingAllowed(contract) ? 'text-primary' : 'text-gray-500'
-                  )}
+            {isFreeResponseContractPage && (
+              <div className={'sm:hidden'}>
+                <button
+                  className={'text-xs font-bold text-gray-500 hover:underline'}
+                  onClick={() => scrollAndOpenReplyInput(undefined, answer)}
                 >
-                  {probPercent}
-                </span>
-                <BuyButton
-                  className={clsx(
-                    'btn-sm flex-initial !px-6 sm:flex',
-                    tradingAllowed(contract) ? '' : '!hidden'
-                  )}
-                  onClick={() => setOpen(true)}
-                />
+                  Reply
+                </button>
               </div>
-            </Row>
+            )}
           </Col>
           {isFreeResponseContractPage && (
             <div className={'justify-initial hidden sm:block'}>
@@ -207,9 +162,9 @@ export function FeedAnswerCommentGroup(props: {
       />
 
       {showReply && (
-        <div className={'ml-6 pt-4'}>
+        <div className={'ml-6'}>
           <span
-            className="absolute -ml-[1px] mt-[0.8rem] h-2 w-0.5 rotate-90 bg-gray-200"
+            className="absolute -ml-[1px] mt-[1.25rem] h-2 w-0.5 rotate-90 bg-gray-200"
             aria-hidden="true"
           />
           <CommentInput
diff --git a/web/components/feed/feed-bets.tsx b/web/components/feed/feed-bets.tsx
index 1520e57c..408404ba 100644
--- a/web/components/feed/feed-bets.tsx
+++ b/web/components/feed/feed-bets.tsx
@@ -93,6 +93,24 @@ export function BetStatusText(props: {
       bet.fills?.some((fill) => fill.matchedBetId === null)) ??
     false
 
+  const fromProb =
+    hadPoolMatch || isFreeResponse
+      ? isPseudoNumeric
+        ? formatNumericProbability(bet.probBefore, contract)
+        : formatPercent(bet.probBefore)
+      : isPseudoNumeric
+      ? formatNumericProbability(bet.limitProb ?? bet.probBefore, contract)
+      : formatPercent(bet.limitProb ?? bet.probBefore)
+
+  const toProb =
+    hadPoolMatch || isFreeResponse
+      ? isPseudoNumeric
+        ? formatNumericProbability(bet.probAfter, contract)
+        : formatPercent(bet.probAfter)
+      : isPseudoNumeric
+      ? formatNumericProbability(bet.limitProb ?? bet.probAfter, contract)
+      : formatPercent(bet.limitProb ?? bet.probAfter)
+
   return (
     <div className="text-sm text-gray-500">
       {bettor ? (
@@ -112,14 +130,9 @@ export function BetStatusText(props: {
             contract={contract}
             truncate="short"
           />{' '}
-          {isPseudoNumeric
-            ? ' than ' + formatNumericProbability(bet.probAfter, contract)
-            : ' at ' +
-              formatPercent(
-                hadPoolMatch || isFreeResponse
-                  ? bet.probAfter
-                  : bet.limitProb ?? bet.probAfter
-              )}
+          {fromProb === toProb
+            ? `at ${fromProb}`
+            : `from ${fromProb} to ${toProb}`}
         </>
       )}
       <RelativeTimestamp time={createdTime} />
diff --git a/web/components/feed/feed-comments.tsx b/web/components/feed/feed-comments.tsx
index 195c5343..f4c6eb74 100644
--- a/web/components/feed/feed-comments.tsx
+++ b/web/components/feed/feed-comments.tsx
@@ -70,7 +70,7 @@ export function FeedCommentThread(props: {
     if (showReply && inputRef) inputRef.focus()
   }, [inputRef, showReply])
   return (
-    <div className={'w-full flex-col pr-1'}>
+    <Col className={'w-full gap-3 pr-1'}>
       <span
         className="absolute top-5 left-5 -ml-px h-[calc(100%-2rem)] w-0.5 bg-gray-200"
         aria-hidden="true"
@@ -86,7 +86,7 @@ export function FeedCommentThread(props: {
         scrollAndOpenReplyInput={scrollAndOpenReplyInput}
       />
       {showReply && (
-        <div className={'-pb-2 ml-6 flex flex-col pt-5'}>
+        <Col className={'-pb-2 ml-6'}>
           <span
             className="absolute -ml-[1px] mt-[0.8rem] h-2 w-0.5 rotate-90 bg-gray-200"
             aria-hidden="true"
@@ -106,9 +106,9 @@ export function FeedCommentThread(props: {
               setReplyToUsername('')
             }}
           />
-        </div>
+        </Col>
       )}
-    </div>
+    </Col>
   )
 }
 
@@ -142,7 +142,7 @@ export function CommentRepliesList(props: {
           id={comment.id}
           className={clsx(
             'relative',
-            !treatFirstIndexEqually && commentIdx === 0 ? '' : 'mt-3 ml-6'
+            !treatFirstIndexEqually && commentIdx === 0 ? '' : 'ml-6'
           )}
         >
           {/*draw a gray line from the comment to the left:*/}
diff --git a/web/components/feed/feed-items.tsx b/web/components/feed/feed-items.tsx
index ff5f5440..ea8302b8 100644
--- a/web/components/feed/feed-items.tsx
+++ b/web/components/feed/feed-items.tsx
@@ -23,6 +23,7 @@ import BetRow from '../bet-row'
 import { Avatar } from '../avatar'
 import { ActivityItem } from './activity-items'
 import { useSaveSeenContract } from 'web/hooks/use-seen-contracts'
+import { useUser } from 'web/hooks/use-user'
 import { trackClick } from 'web/lib/firebase/tracking'
 import { DAY_MS } from 'common/util/time'
 import NewContractBadge from '../new-contract-badge'
@@ -118,6 +119,7 @@ export function FeedQuestion(props: {
   const { volumeLabel } = contractMetrics(contract)
   const isBinary = outcomeType === 'BINARY'
   const isNew = createdTime > Date.now() - DAY_MS && !isResolved
+  const user = useUser()
 
   return (
     <div className={'flex gap-2'}>
@@ -149,7 +151,7 @@ export function FeedQuestion(props: {
             href={
               props.contractPath ? props.contractPath : contractPath(contract)
             }
-            onClick={() => trackClick(contract.id)}
+            onClick={() => user && trackClick(user.id, contract.id)}
             className="text-lg text-indigo-700 sm:text-xl"
           >
             {question}
diff --git a/web/components/filter-select-users.tsx b/web/components/filter-select-users.tsx
index 7ce73cf8..a19ab6af 100644
--- a/web/components/filter-select-users.tsx
+++ b/web/components/filter-select-users.tsx
@@ -7,6 +7,7 @@ import { Menu, Transition } from '@headlessui/react'
 import { Avatar } from 'web/components/avatar'
 import { Row } from 'web/components/layout/row'
 import { UserLink } from 'web/components/user-page'
+import { searchInAny } from 'common/util/parse'
 
 export function FilterSelectUsers(props: {
   setSelectedUsers: (users: User[]) => void
@@ -35,8 +36,7 @@ export function FilterSelectUsers(props: {
           return (
             !selectedUsers.map((user) => user.name).includes(user.name) &&
             !ignoreUserIds.includes(user.id) &&
-            (user.name.toLowerCase().includes(query.toLowerCase()) ||
-              user.username.toLowerCase().includes(query.toLowerCase()))
+            searchInAny(query, user.name, user.username)
           )
         })
       )
diff --git a/web/components/groups/contract-groups-list.tsx b/web/components/groups/contract-groups-list.tsx
new file mode 100644
index 00000000..423cbb97
--- /dev/null
+++ b/web/components/groups/contract-groups-list.tsx
@@ -0,0 +1,73 @@
+import { Col } from 'web/components/layout/col'
+import { Row } from 'web/components/layout/row'
+import clsx from 'clsx'
+import { GroupLinkItem } from 'web/pages/groups'
+import { XIcon } from '@heroicons/react/outline'
+import { Button } from 'web/components/button'
+import { GroupSelector } from 'web/components/groups/group-selector'
+import {
+  addContractToGroup,
+  removeContractFromGroup,
+} from 'web/lib/firebase/groups'
+import { User } from 'common/user'
+import { Contract } from 'common/contract'
+import { SiteLink } from 'web/components/site-link'
+import { GroupLink } from 'common/group'
+import { useGroupsWithContract } from 'web/hooks/use-group'
+
+export function ContractGroupsList(props: {
+  groupLinks: GroupLink[]
+  contract: Contract
+  user: User | null | undefined
+}) {
+  const { groupLinks, user, contract } = props
+  const groups = useGroupsWithContract(contract)
+  return (
+    <Col className={'gap-2'}>
+      <span className={'text-xl text-indigo-700'}>
+        <SiteLink href={'/groups/'}>Groups</SiteLink>
+      </span>
+      {user && (
+        <Col className={'ml-2 items-center justify-between sm:flex-row'}>
+          <span>Add to: </span>
+          <GroupSelector
+            options={{
+              showSelector: true,
+              showLabel: false,
+              ignoreGroupIds: groupLinks.map((g) => g.groupId),
+            }}
+            setSelectedGroup={(group) =>
+              group && addContractToGroup(group, contract, user.id)
+            }
+            selectedGroup={undefined}
+            creator={user}
+          />
+        </Col>
+      )}
+      {groups.length === 0 && (
+        <Col className="ml-2 h-full justify-center text-gray-500">
+          No groups yet...
+        </Col>
+      )}
+      {groups.map((group) => (
+        <Row
+          key={group.id}
+          className={clsx('items-center justify-between gap-2 p-2')}
+        >
+          <Row className="line-clamp-1 items-center gap-2">
+            <GroupLinkItem group={group} />
+          </Row>
+          {user && group.memberIds.includes(user.id) && (
+            <Button
+              color={'gray-white'}
+              size={'xs'}
+              onClick={() => removeContractFromGroup(group, contract)}
+            >
+              <XIcon className="h-4 w-4 text-gray-500" />
+            </Button>
+          )}
+        </Row>
+      ))}
+    </Col>
+  )
+}
diff --git a/web/components/groups/group-selector.tsx b/web/components/groups/group-selector.tsx
index ea1597f2..e6270a4d 100644
--- a/web/components/groups/group-selector.tsx
+++ b/web/components/groups/group-selector.tsx
@@ -11,25 +11,28 @@ import { CreateGroupButton } from 'web/components/groups/create-group-button'
 import { useState } from 'react'
 import { useMemberGroups } from 'web/hooks/use-group'
 import { User } from 'common/user'
+import { searchInAny } from 'common/util/parse'
 
 export function GroupSelector(props: {
-  selectedGroup?: Group
+  selectedGroup: Group | undefined
   setSelectedGroup: (group: Group) => void
   creator: User | null | undefined
-  showSelector?: boolean
+  options: {
+    showSelector: boolean
+    showLabel: boolean
+    ignoreGroupIds?: string[]
+  }
 }) {
-  const { selectedGroup, setSelectedGroup, creator, showSelector } = props
+  const { selectedGroup, setSelectedGroup, creator, options } = props
   const [isCreatingNewGroup, setIsCreatingNewGroup] = useState(false)
-
+  const { showSelector, showLabel, ignoreGroupIds } = options
   const [query, setQuery] = useState('')
-  const memberGroups = useMemberGroups(creator?.id)
-  const filteredGroups = memberGroups
-    ? query === ''
-      ? memberGroups
-      : memberGroups.filter((group) => {
-          return group.name.toLowerCase().includes(query.toLowerCase())
-        })
-    : []
+  const memberGroups = (useMemberGroups(creator?.id) ?? []).filter(
+    (group) => !ignoreGroupIds?.includes(group.id)
+  )
+  const filteredGroups = memberGroups.filter((group) =>
+    searchInAny(query, group.name)
+  )
 
   if (!showSelector || !creator) {
     return (
@@ -56,19 +59,20 @@ export function GroupSelector(props: {
         nullable={true}
         className={'text-sm'}
       >
-        {({ open }) => (
+        {() => (
           <>
-            {!open && setQuery('')}
-            <Combobox.Label className="label justify-start gap-2 text-base">
-              Add to Group
-              <InfoTooltip text="Question will be displayed alongside the other questions in the group." />
-            </Combobox.Label>
+            {showLabel && (
+              <Combobox.Label className="label justify-start gap-2 text-base">
+                Add to Group
+                <InfoTooltip text="Question will be displayed alongside the other questions in the group." />
+              </Combobox.Label>
+            )}
             <div className="relative mt-2">
               <Combobox.Input
-                className="w-full rounded-md border border-gray-300 bg-white p-3 pl-4 pr-20 text-sm shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 "
+                className="w-60 rounded-md border border-gray-300 bg-white p-3 pl-4 pr-20 text-sm shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 "
                 onChange={(event) => setQuery(event.target.value)}
                 displayValue={(group: Group) => group && group.name}
-                placeholder={'None'}
+                placeholder={'E.g. Science, Politics'}
               />
               <Combobox.Button className="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-none">
                 <SelectorIcon
diff --git a/web/components/groups/groups-button.tsx b/web/components/groups/groups-button.tsx
index f3ae77a2..bb94c9ed 100644
--- a/web/components/groups/groups-button.tsx
+++ b/web/components/groups/groups-button.tsx
@@ -11,13 +11,15 @@ import { Modal } from 'web/components/layout/modal'
 import { Col } from 'web/components/layout/col'
 import { joinGroup, leaveGroup } from 'web/lib/firebase/groups'
 import { firebaseLogin } from 'web/lib/firebase/users'
-import { GroupLink } from 'web/pages/groups'
+import { GroupLinkItem } from 'web/pages/groups'
 import toast from 'react-hot-toast'
 
 export function GroupsButton(props: { user: User }) {
   const { user } = props
   const [isOpen, setIsOpen] = useState(false)
-  const groups = useMemberGroups(user.id)
+  const groups = useMemberGroups(user.id, undefined, {
+    by: 'mostRecentChatActivityTime',
+  })
 
   return (
     <>
@@ -75,7 +77,7 @@ function GroupItem(props: { group: Group; className?: string }) {
   return (
     <Row className={clsx('items-center justify-between gap-2 p-2', className)}>
       <Row className="line-clamp-1 items-center gap-2">
-        <GroupLink group={group} />
+        <GroupLinkItem group={group} />
       </Row>
       <JoinOrLeaveGroupButton group={group} />
     </Row>
@@ -133,7 +135,7 @@ export function JoinOrLeaveGroupButton(props: {
     return (
       <button
         className={clsx(
-          'btn btn-outline btn-sm',
+          'btn btn-outline btn-xs',
           small && smallStyle,
           className
         )}
diff --git a/web/components/info-box.tsx b/web/components/info-box.tsx
new file mode 100644
index 00000000..34f65089
--- /dev/null
+++ b/web/components/info-box.tsx
@@ -0,0 +1,30 @@
+import clsx from 'clsx'
+import { InformationCircleIcon } from '@heroicons/react/solid'
+
+import { Linkify } from './linkify'
+
+export function InfoBox(props: {
+  title: string
+  text: string
+  className?: string
+}) {
+  const { title, text, className } = props
+  return (
+    <div className={clsx('rounded-md bg-gray-50 p-4', className)}>
+      <div className="flex">
+        <div className="flex-shrink-0">
+          <InformationCircleIcon
+            className="h-5 w-5 text-gray-400"
+            aria-hidden="true"
+          />
+        </div>
+        <div className="ml-3">
+          <h3 className="text-sm font-medium text-black">{title}</h3>
+          <div className="mt-2 text-sm text-black">
+            <Linkify text={text} />
+          </div>
+        </div>
+      </div>
+    </div>
+  )
+}
diff --git a/web/components/layout/modal.tsx b/web/components/layout/modal.tsx
index 7a320f24..af2b66de 100644
--- a/web/components/layout/modal.tsx
+++ b/web/components/layout/modal.tsx
@@ -7,9 +7,17 @@ export function Modal(props: {
   children: ReactNode
   open: boolean
   setOpen: (open: boolean) => void
+  size?: 'sm' | 'md' | 'lg' | 'xl'
   className?: string
 }) {
-  const { children, open, setOpen, className } = props
+  const { children, open, setOpen, size = 'md', className } = props
+
+  const sizeClass = {
+    sm: 'max-w-sm',
+    md: 'max-w-md',
+    lg: 'max-w-2xl',
+    xl: 'max-w-5xl',
+  }[size]
 
   return (
     <Transition.Root show={open} as={Fragment}>
@@ -49,7 +57,8 @@ export function Modal(props: {
           >
             <div
               className={clsx(
-                'inline-block transform overflow-hidden text-left align-bottom transition-all sm:my-8 sm:w-full sm:max-w-md sm:p-6 sm:align-middle',
+                'my-8 mx-6 inline-block w-full transform overflow-hidden text-left align-bottom transition-all sm:align-middle',
+                sizeClass,
                 className
               )}
             >
diff --git a/web/components/limit-bets.tsx b/web/components/limit-bets.tsx
index 93647a5e..8c9f4e6b 100644
--- a/web/components/limit-bets.tsx
+++ b/web/components/limit-bets.tsx
@@ -1,4 +1,3 @@
-import clsx from 'clsx'
 import { LimitBet } from 'common/bet'
 import { CPMMBinaryContract, PseudoNumericContract } from 'common/contract'
 import { getFormattedMappedValue } from 'common/pseudo-numeric'
@@ -8,10 +7,14 @@ import { useState } from 'react'
 import { useUser, useUserById } from 'web/hooks/use-user'
 import { cancelBet } from 'web/lib/firebase/api'
 import { Avatar } from './avatar'
+import { Button } from './button'
 import { Col } from './layout/col'
-import { Tabs } from './layout/tabs'
+import { Modal } from './layout/modal'
+import { Row } from './layout/row'
 import { LoadingIndicator } from './loading-indicator'
 import { BinaryOutcomeLabel, PseudoNumericOutcomeLabel } from './outcome-label'
+import { Subtitle } from './subtitle'
+import { Title } from './title'
 
 export function LimitBets(props: {
   contract: CPMMBinaryContract | PseudoNumericContract
@@ -28,40 +31,36 @@ export function LimitBets(props: {
   const yourBets = sortedBets.filter((bet) => bet.userId === user?.id)
 
   return (
-    <Col
-      className={clsx(
-        className,
-        'gap-2 overflow-hidden rounded bg-white px-4 py-3'
+    <Col className={className}>
+      {yourBets.length === 0 && (
+        <OrderBookButton
+          className="self-end"
+          limitBets={sortedBets}
+          contract={contract}
+        />
+      )}
+
+      {yourBets.length > 0 && (
+        <Col
+          className={'mt-4 gap-2 overflow-hidden rounded bg-white px-4 py-3'}
+        >
+          <Row className="mt-2 mb-4 items-center justify-between">
+            <Subtitle className="!mt-0 !mb-0" text="Your orders" />
+
+            <OrderBookButton
+              className="self-end"
+              limitBets={sortedBets}
+              contract={contract}
+            />
+          </Row>
+
+          <LimitOrderTable
+            limitBets={yourBets}
+            contract={contract}
+            isYou={true}
+          />
+        </Col>
       )}
-    >
-      <Tabs
-        tabs={[
-          ...(yourBets.length > 0
-            ? [
-                {
-                  title: 'Your limit orders',
-                  content: (
-                    <LimitOrderTable
-                      limitBets={yourBets}
-                      contract={contract}
-                      isYou={true}
-                    />
-                  ),
-                },
-              ]
-            : []),
-          {
-            title: 'All limit orders',
-            content: (
-              <LimitOrderTable
-                limitBets={sortedBets}
-                contract={contract}
-                isYou={false}
-              />
-            ),
-          },
-        ]}
-      />
     </Col>
   )
 }
@@ -77,11 +76,13 @@ export function LimitOrderTable(props: {
   return (
     <table className="table-compact table w-full rounded text-gray-500">
       <thead>
-        {!isYou && <th></th>}
-        <th>Outcome</th>
-        <th>Amount</th>
-        <th>{isPseudoNumeric ? 'Value' : 'Prob'}</th>
-        {isYou && <th></th>}
+        <tr>
+          {!isYou && <th></th>}
+          <th>Outcome</th>
+          <th>{isPseudoNumeric ? 'Value' : 'Prob'}</th>
+          <th>Amount</th>
+          {isYou && <th></th>}
+        </tr>
       </thead>
       <tbody>
         {limitBets.map((bet) => (
@@ -130,12 +131,12 @@ function LimitBet(props: {
           )}
         </div>
       </td>
-      <td>{formatMoney(orderAmount - amount)}</td>
       <td>
         {isPseudoNumeric
           ? getFormattedMappedValue(contract)(limitProb)
           : formatPercent(limitProb)}
       </td>
+      <td>{formatMoney(orderAmount - amount)}</td>
       {isYou && (
         <td>
           {isCancelling ? (
@@ -153,3 +154,53 @@ function LimitBet(props: {
     </tr>
   )
 }
+
+export function OrderBookButton(props: {
+  limitBets: LimitBet[]
+  contract: CPMMBinaryContract | PseudoNumericContract
+  className?: string
+}) {
+  const { limitBets, contract, className } = props
+  const [open, setOpen] = useState(false)
+
+  const yesBets = limitBets.filter((bet) => bet.outcome === 'YES')
+  const noBets = limitBets.filter((bet) => bet.outcome === 'NO').reverse()
+
+  return (
+    <>
+      <Button
+        className={className}
+        onClick={() => setOpen(true)}
+        size="xs"
+        color="blue"
+      >
+        Order book
+      </Button>
+
+      <Modal open={open} setOpen={setOpen} size="lg">
+        <Col className="rounded bg-white p-4 py-6">
+          <Title className="!mt-0" text="Order book" />
+          <Row className="hidden items-start justify-start gap-2 md:flex">
+            <LimitOrderTable
+              limitBets={yesBets}
+              contract={contract}
+              isYou={false}
+            />
+            <LimitOrderTable
+              limitBets={noBets}
+              contract={contract}
+              isYou={false}
+            />
+          </Row>
+          <Col className="md:hidden">
+            <LimitOrderTable
+              limitBets={limitBets}
+              contract={contract}
+              isYou={false}
+            />
+          </Col>
+        </Col>
+      </Modal>
+    </>
+  )
+}
diff --git a/web/components/manalink-card.tsx b/web/components/manalink-card.tsx
index fec05919..51880f5d 100644
--- a/web/components/manalink-card.tsx
+++ b/web/components/manalink-card.tsx
@@ -3,7 +3,13 @@ import { formatMoney } from 'common/util/format'
 import { fromNow } from 'web/lib/util/time'
 import { Col } from 'web/components/layout/col'
 import { Row } from 'web/components/layout/row'
-
+import { Claim, Manalink } from 'common/manalink'
+import { useState } from 'react'
+import { ShareIconButton } from './share-icon-button'
+import { DotsHorizontalIcon } from '@heroicons/react/solid'
+import { contractDetailsButtonClassName } from './contract/contract-info-dialog'
+import { useUserById } from 'web/hooks/use-user'
+import getManalinkUrl from 'web/get-manalink-url'
 export type ManalinkInfo = {
   expiresTime: number | null
   maxUses: number | null
@@ -13,98 +19,202 @@ export type ManalinkInfo = {
 }
 
 export function ManalinkCard(props: {
-  className?: string
   info: ManalinkInfo
-  defaultMessage: string
-  isClaiming: boolean
-  onClaim?: () => void
+  className?: string
+  preview?: boolean
 }) {
-  const { className, defaultMessage, isClaiming, info, onClaim } = props
+  const { className, info, preview = false } = props
   const { expiresTime, maxUses, uses, amount, message } = info
   return (
-    <div
-      className={clsx(
-        className,
-        'min-h-20 group flex flex-col rounded-xl bg-gradient-to-br from-indigo-200 via-indigo-400 to-indigo-800 shadow-lg transition-all'
-      )}
-    >
-      <Col className="mx-4 mt-2 -mb-4 text-right text-sm text-gray-100">
-        <div>
-          {maxUses != null
-            ? `${maxUses - uses}/${maxUses} uses left`
-            : `Unlimited use`}
-        </div>
-        <div>
-          {expiresTime != null
-            ? `Expires ${fromNow(expiresTime)}`
-            : 'Never expires'}
-        </div>
-      </Col>
+    <Col>
+      <Col
+        className={clsx(
+          className,
+          'min-h-20 group rounded-lg bg-gradient-to-br drop-shadow-sm transition-all',
+          getManalinkGradient(info.amount)
+        )}
+      >
+        <Col className="mx-4 mt-2 -mb-4 text-right text-sm text-gray-100">
+          <div>
+            {maxUses != null
+              ? `${maxUses - uses}/${maxUses} uses left`
+              : `Unlimited use`}
+          </div>
+          <div>
+            {expiresTime != null
+              ? `Expires ${fromNow(expiresTime)}`
+              : 'Never expires'}
+          </div>
+        </Col>
 
-      <img
-        className="mb-6 block self-center transition-all group-hover:rotate-12"
-        src="/logo-white.svg"
-        width={200}
-        height={200}
-      />
-      <Row className="justify-end rounded-b-xl bg-white p-4">
-        <Col>
-          <div className="mb-1 text-xl text-indigo-500">
+        <img
+          className={clsx(
+            'block h-1/3 w-1/3 self-center transition-all group-hover:rotate-12',
+            preview ? 'my-2' : 'w-1/2 md:mb-6 md:h-1/2'
+          )}
+          src="/logo-white.svg"
+        />
+        <Row className="rounded-b-lg bg-white p-4">
+          <div
+            className={clsx(
+              'mb-1 text-xl text-indigo-500',
+              getManalinkAmountColor(amount)
+            )}
+          >
             {formatMoney(amount)}
           </div>
-          <div>{message || defaultMessage}</div>
-        </Col>
-
-        <div className="ml-auto">
-          <button
-            className={clsx('btn', isClaiming ? 'loading disabled' : '')}
-            onClick={onClaim}
-          >
-            {isClaiming ? '' : 'Claim'}
-          </button>
-        </div>
-      </Row>
-    </div>
+        </Row>
+      </Col>
+      <div className="text-md mt-2 mb-4 text-gray-500">{message}</div>
+    </Col>
   )
 }
 
-export function ManalinkCardPreview(props: {
+export function ManalinkCardFromView(props: {
   className?: string
-  info: ManalinkInfo
-  defaultMessage: string
+  link: Manalink
+  highlightedSlug: string
 }) {
-  const { className, defaultMessage, info } = props
-  const { expiresTime, maxUses, uses, amount, message } = info
+  const { className, link, highlightedSlug } = props
+  const { message, amount, expiresTime, maxUses, claims } = link
+  const [showDetails, setShowDetails] = useState(false)
+
   return (
-    <div
-      className={clsx(
-        className,
-        ' group flex flex-col rounded-lg bg-gradient-to-br from-indigo-200 via-indigo-400 to-indigo-800 shadow-lg transition-all'
-      )}
-    >
-      <Col className="mx-4 mt-2 -mb-4 text-right text-xs text-gray-100">
-        <div>
-          {maxUses != null
-            ? `${maxUses - uses}/${maxUses} uses left`
-            : `Unlimited use`}
+    <Col>
+      <Col
+        className={clsx(
+          'group z-10 rounded-lg drop-shadow-sm transition-all hover:drop-shadow-lg',
+          className,
+          link.slug === highlightedSlug ? 'shadow-md shadow-indigo-400' : ''
+        )}
+      >
+        <Col
+          className={clsx(
+            'relative rounded-t-lg bg-gradient-to-br transition-all',
+            getManalinkGradient(link.amount)
+          )}
+          onClick={() => setShowDetails(!showDetails)}
+        >
+          {showDetails && (
+            <ClaimsList
+              className="absolute h-full w-full bg-white opacity-90"
+              link={link}
+            />
+          )}
+          <Col className="mx-4 mt-2 -mb-4 text-right text-xs text-gray-100">
+            <div>
+              {maxUses != null
+                ? `${maxUses - claims.length}/${maxUses} uses left`
+                : `Unlimited use`}
+            </div>
+            <div>
+              {expiresTime != null
+                ? `Expires ${fromNow(expiresTime)}`
+                : 'Never expires'}
+            </div>
+          </Col>
+          <img
+            className={clsx('my-auto block w-1/3 select-none self-center py-3')}
+            src="/logo-white.svg"
+          />
+        </Col>
+        <Row className="relative w-full gap-1 rounded-b-lg bg-white px-4 py-2 text-lg">
+          <div
+            className={clsx(
+              'my-auto mb-1 w-full',
+              getManalinkAmountColor(amount)
+            )}
+          >
+            {formatMoney(amount)}
+          </div>
+          <ShareIconButton
+            toastClassName={'-left-48 min-w-[250%]'}
+            buttonClassName={'transition-colors'}
+            onCopyButtonClassName={
+              'bg-gray-200 text-gray-600 transition-none hover:bg-gray-200 hover:text-gray-600'
+            }
+            copyPayload={getManalinkUrl(link.slug)}
+          />
+          <button
+            onClick={() => setShowDetails(!showDetails)}
+            className={clsx(
+              contractDetailsButtonClassName,
+              showDetails
+                ? 'bg-gray-200 text-gray-600 hover:bg-gray-200 hover:text-gray-600'
+                : ''
+            )}
+          >
+            <DotsHorizontalIcon className="h-[24px] w-5" />
+          </button>
+        </Row>
+      </Col>
+      <div className="mt-2 mb-4 text-xs text-gray-500 md:text-sm">
+        {message || ''}
+      </div>
+    </Col>
+  )
+}
+
+function ClaimsList(props: { link: Manalink; className: string }) {
+  const { link, className } = props
+  return (
+    <>
+      <Col className={clsx('px-4 py-2', className)}>
+        <div className="text-md mb-1 mt-2 w-full font-semibold">
+          Claimed by...
         </div>
-        <div>
-          {expiresTime != null
-            ? `Expires ${fromNow(expiresTime)}`
-            : 'Never expires'}
+        <div className="overflow-auto">
+          {link.claims.length > 0 ? (
+            <>
+              {link.claims.map((claim) => (
+                <Row key={claim.txnId}>
+                  <Claim claim={claim} />
+                </Row>
+              ))}
+            </>
+          ) : (
+            <div className="h-full">
+              No one has claimed this manalink yet! Share your manalink to start
+              spreading the wealth.
+            </div>
+          )}
         </div>
       </Col>
-
-      <img
-        className="my-2 block h-1/3 w-1/3 self-center transition-all group-hover:rotate-12"
-        src="/logo-white.svg"
-      />
-      <Row className="rounded-b-lg bg-white p-2">
-        <Col className="text-md">
-          <div className="mb-1 text-indigo-500">{formatMoney(amount)}</div>
-          <div className="text-xs">{message || defaultMessage}</div>
-        </Col>
-      </Row>
-    </div>
+    </>
   )
 }
+
+function Claim(props: { claim: Claim }) {
+  const { claim } = props
+  const who = useUserById(claim.toId)
+  return (
+    <Row className="my-1 gap-2 text-xs">
+      <div>{who?.name || 'Loading...'}</div>
+      <div className="text-gray-500">{fromNow(claim.claimedTime)}</div>
+    </Row>
+  )
+}
+
+function getManalinkGradient(amount: number) {
+  if (amount < 20) {
+    return 'from-indigo-200 via-indigo-500 to-indigo-800'
+  } else if (amount >= 20 && amount < 50) {
+    return 'from-fuchsia-200 via-fuchsia-500 to-fuchsia-800'
+  } else if (amount >= 50 && amount < 100) {
+    return 'from-rose-100 via-rose-400 to-rose-700'
+  } else if (amount >= 100) {
+    return 'from-amber-200 via-amber-500 to-amber-700'
+  }
+}
+
+function getManalinkAmountColor(amount: number) {
+  if (amount < 20) {
+    return 'text-indigo-500'
+  } else if (amount >= 20 && amount < 50) {
+    return 'text-fuchsia-600'
+  } else if (amount >= 50 && amount < 100) {
+    return 'text-rose-600'
+  } else if (amount >= 100) {
+    return 'text-amber-600'
+  }
+}
diff --git a/web/components/manalinks/create-links-button.tsx b/web/components/manalinks/create-links-button.tsx
index 12ab8c87..656aff29 100644
--- a/web/components/manalinks/create-links-button.tsx
+++ b/web/components/manalinks/create-links-button.tsx
@@ -4,12 +4,12 @@ import { Col } from '../layout/col'
 import { Row } from '../layout/row'
 import { Title } from '../title'
 import { User } from 'common/user'
-import { ManalinkCardPreview, ManalinkInfo } from 'web/components/manalink-card'
+import { ManalinkCard, ManalinkInfo } from 'web/components/manalink-card'
 import { createManalink } from 'web/lib/firebase/manalinks'
 import { Modal } from 'web/components/layout/modal'
 import Textarea from 'react-expanding-textarea'
 import dayjs from 'dayjs'
-import Button from '../button'
+import { Button } from '../button'
 import { getManalinkUrl } from 'web/pages/links'
 import { DuplicateIcon } from '@heroicons/react/outline'
 
@@ -66,12 +66,14 @@ function CreateManalinkForm(props: {
   const defaultExpire = 'week'
   const [expiresIn, setExpiresIn] = useState(defaultExpire)
 
+  const defaultMessage = 'from ' + user.name
+
   const [newManalink, setNewManalink] = useState<ManalinkInfo>({
     expiresTime: dayjs().add(1, defaultExpire).valueOf(),
     amount: 100,
     maxUses: 1,
     uses: 0,
-    message: '',
+    message: defaultMessage,
   })
 
   const EXPIRE_OPTIONS = {
@@ -161,7 +163,8 @@ function CreateManalinkForm(props: {
             <div className="form-control w-full">
               <label className="label">Message</label>
               <Textarea
-                placeholder={`From ${user.name}`}
+                placeholder={defaultMessage}
+                maxLength={200}
                 className="input input-bordered resize-none"
                 autoFocus
                 value={newManalink.message}
@@ -189,11 +192,7 @@ function CreateManalinkForm(props: {
       {finishedCreating && (
         <>
           <Title className="!my-0" text="Manalink Created!" />
-          <ManalinkCardPreview
-            className="my-4"
-            defaultMessage={`From ${user.name}`}
-            info={newManalink}
-          />
+          <ManalinkCard className="my-4" info={newManalink} preview />
           <Row
             className={clsx(
               'rounded border bg-gray-50 py-2 px-3 text-sm text-gray-500 transition-colors duration-700',
diff --git a/web/components/nav/sidebar.tsx b/web/components/nav/sidebar.tsx
index 979586f9..155a8783 100644
--- a/web/components/nav/sidebar.tsx
+++ b/web/components/nav/sidebar.tsx
@@ -11,7 +11,7 @@ import {
 } from '@heroicons/react/outline'
 import clsx from 'clsx'
 import Link from 'next/link'
-import { useRouter } from 'next/router'
+import Router, { useRouter } from 'next/router'
 import { usePrivateUser, useUser } from 'web/hooks/use-user'
 import { firebaseLogout, User } from 'web/lib/firebase/users'
 import { ManifoldLogo } from './manifold-logo'
@@ -24,13 +24,20 @@ import { CreateQuestionButton } from 'web/components/create-question-button'
 import { useMemberGroups } from 'web/hooks/use-group'
 import { groupPath } from 'web/lib/firebase/groups'
 import { trackCallback, withTracking } from 'web/lib/service/analytics'
-import { Group } from 'common/group'
+import { Group, GROUP_CHAT_SLUG } from 'common/group'
 import { Spacer } from '../layout/spacer'
 import { useUnseenPreferredNotifications } from 'web/hooks/use-notifications'
 import { setNotificationsAsSeen } from 'web/pages/notifications'
 import { PrivateUser } from 'common/user'
 import { useWindowSize } from 'web/hooks/use-window-size'
 
+const logout = async () => {
+  // log out, and then reload the page, in case SSR wants to boot them out
+  // of whatever logged-in-only area of the site they might be in
+  await withTracking(firebaseLogout, 'sign out')()
+  await Router.replace(Router.asPath)
+}
+
 function getNavigation() {
   return [
     { name: 'Home', href: '/home', icon: HomeIcon },
@@ -40,6 +47,8 @@ function getNavigation() {
       icon: NotificationsIcon,
     },
 
+    { name: 'Leaderboards', href: '/leaderboards', icon: TrendingUpIcon },
+
     ...(IS_PRIVATE_MANIFOLD
       ? [
           {
@@ -65,7 +74,6 @@ function getMoreNavigation(user?: User | null) {
 
   if (!user) {
     return [
-      { name: 'Leaderboards', href: '/leaderboards' },
       { name: 'Charity', href: '/charity' },
       { name: 'Blog', href: 'https://news.manifold.markets' },
       { name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh' },
@@ -74,15 +82,15 @@ function getMoreNavigation(user?: User | null) {
   }
 
   return [
-    { name: 'Send M$', href: '/links' },
-    { name: 'Leaderboards', href: '/leaderboards' },
+    { name: 'Referrals', href: '/referrals' },
     { name: 'Charity', href: '/charity' },
+    { name: 'Send M$', href: '/links' },
     { name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh' },
     { name: 'About', href: 'https://docs.manifold.markets/$how-to' },
     {
       name: 'Sign out',
       href: '#',
-      onClick: withTracking(firebaseLogout, 'sign out'),
+      onClick: logout,
     },
   ]
 }
@@ -90,7 +98,6 @@ function getMoreNavigation(user?: User | null) {
 const signedOutNavigation = [
   { name: 'Home', href: '/home', icon: HomeIcon },
   { name: 'Explore', href: '/markets', icon: SearchIcon },
-  { name: 'Charity', href: '/charity', icon: HeartIcon },
   {
     name: 'About',
     href: 'https://docs.manifold.markets/$how-to',
@@ -110,6 +117,7 @@ const signedOutMobileNavigation = [
 ]
 
 const signedInMobileNavigation = [
+  { name: 'Leaderboards', href: '/leaderboards', icon: TrendingUpIcon },
   ...(IS_PRIVATE_MANIFOLD
     ? []
     : [{ name: 'Get M$', href: '/add-funds', icon: CashIcon }]),
@@ -125,15 +133,15 @@ function getMoreMobileNav() {
     ...(IS_PRIVATE_MANIFOLD
       ? []
       : [
-          { name: 'Send M$', href: '/links' },
+          { name: 'Referrals', href: '/referrals' },
           { name: 'Charity', href: '/charity' },
+          { name: 'Send M$', href: '/links' },
           { name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh' },
         ]),
-    { name: 'Leaderboards', href: '/leaderboards' },
     {
       name: 'Sign out',
       href: '#',
-      onClick: withTracking(firebaseLogout, 'sign out'),
+      onClick: logout,
     },
   ]
 }
@@ -205,15 +213,22 @@ export default function Sidebar(props: { className?: string }) {
 
   const user = useUser()
   const privateUser = usePrivateUser(user?.id)
+  // usePing(user?.id)
+
   const navigationOptions = !user ? signedOutNavigation : getNavigation()
   const mobileNavigationOptions = !user
     ? signedOutMobileNavigation
     : signedInMobileNavigation
+
   const memberItems = (
-    useMemberGroups(user?.id, { withChatEnabled: true }) ?? []
+    useMemberGroups(
+      user?.id,
+      { withChatEnabled: true },
+      { by: 'mostRecentChatActivityTime' }
+    ) ?? []
   ).map((group: Group) => ({
     name: group.name,
-    href: groupPath(group.slug),
+    href: `${groupPath(group.slug)}/${GROUP_CHAT_SLUG}`,
   }))
 
   return (
@@ -242,7 +257,10 @@ export default function Sidebar(props: { className?: string }) {
             buttonContent={<MoreButton />}
           />
         )}
-
+        {/* Spacer if there are any groups */}
+        {memberItems.length > 0 && (
+          <hr className="!my-4 mr-2 border-gray-300" />
+        )}
         {privateUser && (
           <GroupsList
             currentPage={router.asPath}
@@ -265,11 +283,7 @@ export default function Sidebar(props: { className?: string }) {
         )}
 
         {/* Spacer if there are any groups */}
-        {memberItems.length > 0 && (
-          <div className="py-3">
-            <div className="h-[1px] bg-gray-300" />
-          </div>
-        )}
+        {memberItems.length > 0 && <hr className="!my-4 border-gray-300" />}
         {privateUser && (
           <GroupsList
             currentPage={router.asPath}
@@ -298,8 +312,18 @@ function GroupsList(props: {
 
   // Set notification as seen if our current page is equal to the isSeenOnHref property
   useEffect(() => {
+    const currentPageWithoutQuery = currentPage.split('?')[0]
+    const currentPageGroupSlug = currentPageWithoutQuery.split('/')[2]
     preferredNotifications.forEach((notification) => {
-      if (notification.isSeenOnHref === currentPage) {
+      if (
+        notification.isSeenOnHref === currentPage ||
+        // Old chat style group chat notif was just /group/slug
+        (notification.isSeenOnHref &&
+          currentPageWithoutQuery.includes(notification.isSeenOnHref)) ||
+        // They're on the home page, so if they've a chat notif, they're seeing the chat
+        (notification.isSeenOnHref?.endsWith(GROUP_CHAT_SLUG) &&
+          currentPageWithoutQuery.endsWith(currentPageGroupSlug))
+      ) {
         setNotificationsAsSeen([notification])
       }
     })
diff --git a/web/components/number-input.tsx b/web/components/number-input.tsx
index d7159fab..0b48df6e 100644
--- a/web/components/number-input.tsx
+++ b/web/components/number-input.tsx
@@ -9,8 +9,8 @@ export function NumberInput(props: {
   numberString: string
   onChange: (newNumberString: string) => void
   error: string | undefined
-  label: string
   disabled?: boolean
+  placeholder?: string
   className?: string
   inputClassName?: string
   // Needed to focus the amount input
@@ -21,8 +21,8 @@ export function NumberInput(props: {
     numberString,
     onChange,
     error,
-    label,
     disabled,
+    placeholder,
     className,
     inputClassName,
     inputRef,
@@ -32,16 +32,17 @@ export function NumberInput(props: {
   return (
     <Col className={className}>
       <label className="input-group">
-        <span className="bg-gray-200 text-sm">{label}</span>
         <input
           className={clsx(
-            'input input-bordered max-w-[200px] text-lg',
+            'input input-bordered max-w-[200px] text-lg placeholder:text-gray-400',
             error && 'input-error',
             inputClassName
           )}
           ref={inputRef}
           type="number"
-          placeholder="0"
+          pattern="[0-9]*"
+          inputMode="numeric"
+          placeholder={placeholder ?? '0'}
           maxLength={9}
           value={numberString}
           disabled={disabled}
diff --git a/web/components/online-user-list.tsx b/web/components/online-user-list.tsx
new file mode 100644
index 00000000..d7f52d56
--- /dev/null
+++ b/web/components/online-user-list.tsx
@@ -0,0 +1,83 @@
+import clsx from 'clsx'
+import { Avatar } from './avatar'
+import { Col } from './layout/col'
+import { Row } from './layout/row'
+import { UserLink } from './user-page'
+import { User } from 'common/user'
+import { UserCircleIcon } from '@heroicons/react/solid'
+import { useUsers } from 'web/hooks/use-users'
+import { partition } from 'lodash'
+import { useWindowSize } from 'web/hooks/use-window-size'
+import { useState } from 'react'
+
+const isOnline = (user?: User) =>
+  user && user.lastPingTime && user.lastPingTime > Date.now() - 5 * 60 * 1000
+
+export function OnlineUserList(props: { users: User[] }) {
+  let { users } = props
+  const liveUsers = useUsers().filter((user) =>
+    users.map((u) => u.id).includes(user.id)
+  )
+  if (liveUsers) users = liveUsers
+  const [onlineUsers, offlineUsers] = partition(users, (user) => isOnline(user))
+  const { width, height } = useWindowSize()
+  const [containerRef, setContainerRef] = useState<HTMLDivElement | null>(null)
+  // Subtract bottom bar when it's showing (less than lg screen)
+  const bottomBarHeight = (width ?? 0) < 1024 ? 58 : 0
+  const remainingHeight =
+    (height ?? 0) - (containerRef?.offsetTop ?? 0) - bottomBarHeight
+  return (
+    <Col
+      className="mt-4 flex-1 gap-1 hover:overflow-auto"
+      ref={setContainerRef}
+      style={{ height: remainingHeight }}
+    >
+      {onlineUsers
+        .concat(
+          offlineUsers.sort(
+            (a, b) => (b.lastPingTime ?? 0) - (a.lastPingTime ?? 0)
+          )
+        )
+        .slice(0, 15)
+        .map((user) => (
+          <Row
+            key={user.id}
+            className={clsx('items-center justify-between gap-2 p-2')}
+          >
+            <OnlineUserAvatar key={user.id} user={user} size={'sm'} />
+          </Row>
+        ))}
+    </Col>
+  )
+}
+
+export function OnlineUserAvatar(props: {
+  user?: User
+  className?: string
+  size?: 'sm' | 'xs' | number
+}) {
+  const { user, className, size } = props
+
+  return (
+    <Row className={clsx('relative items-center gap-2', className)}>
+      <Avatar
+        username={user?.username}
+        avatarUrl={user?.avatarUrl}
+        size={size}
+        className={!isOnline(user) ? 'opacity-50' : ''}
+      />
+      {user && (
+        <UserLink
+          name={user.name}
+          username={user.username}
+          className={!isOnline(user) ? 'text-gray-500' : ''}
+        />
+      )}
+      {isOnline(user) && (
+        <div className="absolute left-0 top-0 ">
+          <UserCircleIcon className="text-primary bg-primary h-3 w-3 rounded-full border-2 border-white" />
+        </div>
+      )}
+    </Row>
+  )
+}
diff --git a/web/components/page.tsx b/web/components/page.tsx
index e76a4dc2..1913eb7a 100644
--- a/web/components/page.tsx
+++ b/web/components/page.tsx
@@ -8,9 +8,11 @@ export function Page(props: {
   rightSidebar?: ReactNode
   suspend?: boolean
   className?: string
+  rightSidebarClassName?: string
   children?: ReactNode
 }) {
-  const { children, rightSidebar, suspend, className } = props
+  const { children, rightSidebar, suspend, className, rightSidebarClassName } =
+    props
 
   const bottomBarPadding = 'pb-[58px] lg:pb-0 '
   return (
@@ -37,7 +39,11 @@ export function Page(props: {
           <div className="block xl:hidden">{rightSidebar}</div>
         </main>
         <aside className="hidden xl:col-span-3 xl:block">
-          <div className="sticky top-4 space-y-4">{rightSidebar}</div>
+          <div
+            className={clsx('sticky top-4 space-y-4', rightSidebarClassName)}
+          >
+            {rightSidebar}
+          </div>
         </aside>
       </div>
 
@@ -56,4 +62,6 @@ const visuallyHiddenStyle = {
   position: 'absolute',
   width: 1,
   whiteSpace: 'nowrap',
+  userSelect: 'none',
+  visibility: 'hidden',
 } as const
diff --git a/web/components/pagination.tsx b/web/components/pagination.tsx
index a585985d..3f4108bc 100644
--- a/web/components/pagination.tsx
+++ b/web/components/pagination.tsx
@@ -1,17 +1,34 @@
+import clsx from 'clsx'
+
 export function Pagination(props: {
   page: number
   itemsPerPage: number
   totalItems: number
   setPage: (page: number) => void
   scrollToTop?: boolean
+  className?: string
+  nextTitle?: string
+  prevTitle?: string
 }) {
-  const { page, itemsPerPage, totalItems, setPage, scrollToTop } = props
+  const {
+    page,
+    itemsPerPage,
+    totalItems,
+    setPage,
+    scrollToTop,
+    nextTitle,
+    prevTitle,
+    className,
+  } = props
 
   const maxPage = Math.ceil(totalItems / itemsPerPage) - 1
 
   return (
     <nav
-      className="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6"
+      className={clsx(
+        'flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6',
+        className
+      )}
       aria-label="Pagination"
     >
       <div className="hidden sm:block">
@@ -25,19 +42,21 @@ export function Pagination(props: {
         </p>
       </div>
       <div className="flex flex-1 justify-between sm:justify-end">
-        <a
-          href={scrollToTop ? '#' : undefined}
-          className="relative inline-flex cursor-pointer select-none items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50"
-          onClick={() => page > 0 && setPage(page - 1)}
-        >
-          Previous
-        </a>
+        {page > 0 && (
+          <a
+            href={scrollToTop ? '#' : undefined}
+            className="relative inline-flex cursor-pointer select-none items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50"
+            onClick={() => page > 0 && setPage(page - 1)}
+          >
+            {prevTitle ?? 'Previous'}
+          </a>
+        )}
         <a
           href={scrollToTop ? '#' : undefined}
           className="relative ml-3 inline-flex cursor-pointer select-none items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50"
           onClick={() => page < maxPage && setPage(page + 1)}
         >
-          Next
+          {nextTitle ?? 'Next'}
         </a>
       </div>
     </nav>
diff --git a/web/components/portfolio/portfolio-value-section.tsx b/web/components/portfolio/portfolio-value-section.tsx
index 22f1478f..fa50365b 100644
--- a/web/components/portfolio/portfolio-value-section.tsx
+++ b/web/components/portfolio/portfolio-value-section.tsx
@@ -19,6 +19,13 @@ export const PortfolioValueSection = memo(
       return <></>
     }
 
+    // PATCH: If portfolio history started on June 1st, then we label it as "Since June"
+    // instead of "All time"
+    const allTimeLabel =
+      lastPortfolioMetrics.timestamp < Date.parse('2022-06-20T00:00:00.000Z')
+        ? 'Since June'
+        : 'All time'
+
     return (
       <div>
         <Row className="gap-8">
@@ -39,7 +46,7 @@ export const PortfolioValueSection = memo(
               setPortfolioPeriod(e.target.value as Period)
             }}
           >
-            <option value="allTime">All time</option>
+            <option value="allTime">{allTimeLabel}</option>
             <option value="weekly">7 days</option>
             <option value="daily">24 hours</option>
           </select>
diff --git a/web/components/probability-input.tsx b/web/components/probability-input.tsx
index 15f73799..cc8b9259 100644
--- a/web/components/probability-input.tsx
+++ b/web/components/probability-input.tsx
@@ -1,4 +1,7 @@
 import clsx from 'clsx'
+import { CPMMBinaryContract, PseudoNumericContract } from 'common/contract'
+import { getPseudoProbability } from 'common/pseudo-numeric'
+import { BucketInput } from './bucket-input'
 import { Col } from './layout/col'
 import { Spacer } from './layout/spacer'
 
@@ -6,10 +9,12 @@ export function ProbabilityInput(props: {
   prob: number | undefined
   onChange: (newProb: number | undefined) => void
   disabled?: boolean
+  placeholder?: string
   className?: string
   inputClassName?: string
 }) {
-  const { prob, onChange, disabled, className, inputClassName } = props
+  const { prob, onChange, disabled, placeholder, className, inputClassName } =
+    props
 
   const onProbChange = (str: string) => {
     let prob = parseInt(str.replace(/\D/g, ''))
@@ -27,7 +32,7 @@ export function ProbabilityInput(props: {
       <label className="input-group">
         <input
           className={clsx(
-            'input input-bordered max-w-[200px] text-lg',
+            'input input-bordered max-w-[200px] text-lg placeholder:text-gray-400',
             inputClassName
           )}
           type="number"
@@ -35,7 +40,7 @@ export function ProbabilityInput(props: {
           min={1}
           pattern="[0-9]*"
           inputMode="numeric"
-          placeholder="0"
+          placeholder={placeholder ?? '0'}
           maxLength={2}
           value={prob ?? ''}
           disabled={disabled}
@@ -47,3 +52,43 @@ export function ProbabilityInput(props: {
     </Col>
   )
 }
+
+export function ProbabilityOrNumericInput(props: {
+  contract: CPMMBinaryContract | PseudoNumericContract
+  prob: number | undefined
+  setProb: (prob: number | undefined) => void
+  isSubmitting: boolean
+  placeholder?: string
+}) {
+  const { contract, prob, setProb, isSubmitting, placeholder } = props
+  const isPseudoNumeric = contract.outcomeType === 'PSEUDO_NUMERIC'
+
+  return isPseudoNumeric ? (
+    <BucketInput
+      contract={contract}
+      onBucketChange={(value) =>
+        setProb(
+          value === undefined
+            ? undefined
+            : 100 *
+                getPseudoProbability(
+                  value,
+                  contract.min,
+                  contract.max,
+                  contract.isLogScale
+                )
+        )
+      }
+      isSubmitting={isSubmitting}
+      placeholder={placeholder}
+    />
+  ) : (
+    <ProbabilityInput
+      inputClassName="w-full max-w-none"
+      prob={prob}
+      onChange={setProb}
+      disabled={isSubmitting}
+      placeholder={placeholder}
+    />
+  )
+}
diff --git a/web/components/share-icon-button.tsx b/web/components/share-icon-button.tsx
index 507d90c2..4db192a9 100644
--- a/web/components/share-icon-button.tsx
+++ b/web/components/share-icon-button.tsx
@@ -2,65 +2,48 @@ import React, { useState } from 'react'
 import { ShareIcon } from '@heroicons/react/outline'
 import clsx from 'clsx'
 
-import { Contract } from 'common/contract'
 import { copyToClipboard } from 'web/lib/util/copy'
-import { contractPath } from 'web/lib/firebase/contracts'
-import { ENV_CONFIG } from 'common/envs/constants'
 import { ToastClipboard } from 'web/components/toast-clipboard'
 import { track } from 'web/lib/service/analytics'
 import { contractDetailsButtonClassName } from 'web/components/contract/contract-info-dialog'
-import { Group } from 'common/group'
-import { groupPath } from 'web/lib/firebase/groups'
-
-function copyContractWithReferral(contract: Contract, username?: string) {
-  const postFix =
-    username && contract.creatorUsername !== username
-      ? '?referrer=' + username
-      : ''
-  copyToClipboard(
-    `https://${ENV_CONFIG.domain}${contractPath(contract)}${postFix}`
-  )
-}
-
-// Note: if a user arrives at a /group endpoint with a ?referral= query, they'll be added to the group automatically
-function copyGroupWithReferral(group: Group, username?: string) {
-  const postFix = username ? '?referrer=' + username : ''
-  copyToClipboard(
-    `https://${ENV_CONFIG.domain}${groupPath(group.slug)}${postFix}`
-  )
-}
 
 export function ShareIconButton(props: {
-  contract?: Contract
-  group?: Group
   buttonClassName?: string
+  onCopyButtonClassName?: string
   toastClassName?: string
-  username?: string
   children?: React.ReactNode
+  iconClassName?: string
+  copyPayload: string
 }) {
   const {
-    contract,
     buttonClassName,
+    onCopyButtonClassName,
     toastClassName,
-    username,
-    group,
     children,
+    iconClassName,
+    copyPayload,
   } = props
   const [showToast, setShowToast] = useState(false)
 
   return (
     <div className="relative z-10 flex-shrink-0">
       <button
-        className={clsx(contractDetailsButtonClassName, buttonClassName)}
+        className={clsx(
+          contractDetailsButtonClassName,
+          buttonClassName,
+          showToast ? onCopyButtonClassName : ''
+        )}
         onClick={() => {
-          if (contract) copyContractWithReferral(contract, username)
-          if (group) copyGroupWithReferral(group, username)
+          copyToClipboard(copyPayload)
           track('copy share link')
           setShowToast(true)
           setTimeout(() => setShowToast(false), 2000)
         }}
       >
-        <ShareIcon className="h-[24px] w-5" aria-hidden="true" />
+        <ShareIcon
+          className={clsx(iconClassName ? iconClassName : 'h-[24px] w-5')}
+          aria-hidden="true"
+        />
         {children}
       </button>
 
diff --git a/web/components/share-market.tsx b/web/components/share-market.tsx
index a5da585f..be943a34 100644
--- a/web/components/share-market.tsx
+++ b/web/components/share-market.tsx
@@ -1,5 +1,8 @@
 import clsx from 'clsx'
-import { Contract, contractUrl } from 'web/lib/firebase/contracts'
+
+import { ENV_CONFIG } from 'common/envs/constants'
+
+import { Contract, contractPath, contractUrl } from 'web/lib/firebase/contracts'
 import { CopyLinkButton } from './copy-link-button'
 import { Col } from './layout/col'
 import { Row } from './layout/row'
@@ -7,18 +10,15 @@ import { Row } from './layout/row'
 export function ShareMarket(props: { contract: Contract; className?: string }) {
   const { contract, className } = props
 
+  const url = `https://${ENV_CONFIG.domain}${contractPath(contract)}`
+
   return (
     <Col className={clsx(className, 'gap-3')}>
       <div>Share your market</div>
       <Row className="mb-6 items-center">
-        <input
-          className="input input-bordered flex-1 rounded-r-none text-gray-500"
-          readOnly
-          type="text"
-          value={contractUrl(contract)}
-        />
         <CopyLinkButton
-          contract={contract}
+          url={url}
+          displayUrl={contractUrl(contract)}
           buttonClassName="btn-md rounded-l-none"
           toastClassName={'-left-28 mt-1'}
         />
diff --git a/web/components/user-page.tsx b/web/components/user-page.tsx
index be3f3ac4..09c28920 100644
--- a/web/components/user-page.tsx
+++ b/web/components/user-page.tsx
@@ -38,6 +38,8 @@ import { GroupsButton } from 'web/components/groups/groups-button'
 import { PortfolioValueSection } from './portfolio/portfolio-value-section'
 import { filterDefined } from 'common/util/array'
 import { useUserBets } from 'web/hooks/use-user-bets'
+import { ReferralsButton } from 'web/components/referrals-button'
+import { formatMoney } from 'common/util/format'
 
 export function UserLink(props: {
   name: string
@@ -122,6 +124,7 @@ export function UserPage(props: {
 
   const yourFollows = useFollows(currentUser?.id)
   const isFollowing = yourFollows?.includes(user.id)
+  const profit = user.profitCached.allTime
 
   const onFollow = () => {
     if (!currentUser) return
@@ -186,6 +189,17 @@ export function UserPage(props: {
       <Col className="mx-4 -mt-6">
         <span className="text-2xl font-bold">{user.name}</span>
         <span className="text-gray-500">@{user.username}</span>
+        <span className="text-gray-500">
+          <span
+            className={clsx(
+              'text-md',
+              profit >= 0 ? 'text-green-600' : 'text-red-400'
+            )}
+          >
+            {formatMoney(profit)}
+          </span>{' '}
+          profit
+        </span>
 
         <Spacer h={4} />
 
@@ -202,7 +216,9 @@ export function UserPage(props: {
           <Row className="gap-4">
             <FollowingButton user={user} />
             <FollowersButton user={user} />
-            {/* <ReferralsButton user={user} currentUser={currentUser} /> */}
+            {currentUser?.username === 'ian' && (
+              <ReferralsButton user={user} currentUser={currentUser} />
+            )}
             <GroupsButton user={user} />
           </Row>
 
diff --git a/web/components/yes-no-selector.tsx b/web/components/yes-no-selector.tsx
index dda97c0c..3b3cc21d 100644
--- a/web/components/yes-no-selector.tsx
+++ b/web/components/yes-no-selector.tsx
@@ -5,6 +5,68 @@ import { Col } from './layout/col'
 import { Row } from './layout/row'
 import { resolution } from 'common/contract'
 
+export function YesNoSelector(props: {
+  selected?: 'YES' | 'NO'
+  onSelect: (selected: 'YES' | 'NO') => void
+  className?: string
+  btnClassName?: string
+  replaceYesButton?: React.ReactNode
+  replaceNoButton?: React.ReactNode
+  isPseudoNumeric?: boolean
+}) {
+  const {
+    selected,
+    onSelect,
+    className,
+    btnClassName,
+    replaceNoButton,
+    replaceYesButton,
+    isPseudoNumeric,
+  } = props
+
+  const commonClassNames =
+    'inline-flex items-center justify-center rounded-3xl border-2 p-2'
+
+  return (
+    <Row className={clsx('space-x-3', className)}>
+      {replaceYesButton ? (
+        replaceYesButton
+      ) : (
+        <button
+          className={clsx(
+            commonClassNames,
+            'hover:bg-primary-focus border-primary hover:border-primary-focus hover:text-white',
+            selected == 'YES'
+              ? 'bg-primary text-white'
+              : 'text-primary bg-transparent',
+            btnClassName
+          )}
+          onClick={() => onSelect('YES')}
+        >
+          {isPseudoNumeric ? 'HIGHER' : 'YES'}
+        </button>
+      )}
+      {replaceNoButton ? (
+        replaceNoButton
+      ) : (
+        <button
+          className={clsx(
+            commonClassNames,
+            'border-red-400 hover:border-red-500 hover:bg-red-500 hover:text-white',
+            selected == 'NO'
+              ? 'bg-red-400 text-white'
+              : 'bg-transparent text-red-400',
+            btnClassName
+          )}
+          onClick={() => onSelect('NO')}
+        >
+          {isPseudoNumeric ? 'LOWER' : 'NO'}
+        </button>
+      )}
+    </Row>
+  )
+}
+
 export function YesNoCancelSelector(props: {
   selected: resolution | undefined
   onSelect: (selected: resolution) => void
diff --git a/web/hooks/use-algo-feed.ts b/web/hooks/use-algo-feed.ts
index fde50e80..e195936f 100644
--- a/web/hooks/use-algo-feed.ts
+++ b/web/hooks/use-algo-feed.ts
@@ -25,7 +25,7 @@ export const useAlgoFeed = (
           getDefaultFeed().then((feed) => setAllFeed(feed))
         } else setAllFeed(feed)
 
-        trackLatency('feed', getTime())
+        trackLatency(user.id, 'feed', getTime())
         console.log('"all" feed load time', getTime())
       })
 
diff --git a/web/hooks/use-follows.ts b/web/hooks/use-follows.ts
index e5a074d6..2a8caaea 100644
--- a/web/hooks/use-follows.ts
+++ b/web/hooks/use-follows.ts
@@ -5,7 +5,16 @@ export const useFollows = (userId: string | null | undefined) => {
   const [followIds, setFollowIds] = useState<string[] | undefined>()
 
   useEffect(() => {
-    if (userId) return listenForFollows(userId, setFollowIds)
+    if (userId) {
+      const key = `follows:${userId}`
+      const follows = localStorage.getItem(key)
+      if (follows) setFollowIds(JSON.parse(follows))
+
+      return listenForFollows(userId, (follows) => {
+        setFollowIds(follows)
+        localStorage.setItem(key, JSON.stringify(follows))
+      })
+    }
   }, [userId])
 
   return followIds
diff --git a/web/hooks/use-group.ts b/web/hooks/use-group.ts
index c3098ba4..84913962 100644
--- a/web/hooks/use-group.ts
+++ b/web/hooks/use-group.ts
@@ -2,13 +2,15 @@ import { useEffect, useState } from 'react'
 import { Group } from 'common/group'
 import { User } from 'common/user'
 import {
-  getGroupsWithContractId,
   listenForGroup,
   listenForGroups,
   listenForMemberGroups,
+  listGroups,
 } from 'web/lib/firebase/groups'
 import { getUser, getUsers } from 'web/lib/firebase/users'
 import { filterDefined } from 'common/util/array'
+import { Contract } from 'common/contract'
+import { uniq } from 'lodash'
 
 export const useGroup = (groupId: string | undefined) => {
   const [group, setGroup] = useState<Group | null | undefined>()
@@ -32,19 +34,27 @@ export const useGroups = () => {
 
 export const useMemberGroups = (
   userId: string | null | undefined,
-  options?: { withChatEnabled: boolean }
+  options?: { withChatEnabled: boolean },
+  sort?: { by: 'mostRecentChatActivityTime' | 'mostRecentContractAddedTime' }
 ) => {
   const [memberGroups, setMemberGroups] = useState<Group[] | undefined>()
   useEffect(() => {
     if (userId)
-      return listenForMemberGroups(userId, (groups) => {
-        if (options?.withChatEnabled)
-          return setMemberGroups(
-            filterDefined(groups.filter((group) => group.chatDisabled !== true))
-          )
-        return setMemberGroups(groups)
-      })
-  }, [options?.withChatEnabled, userId])
+      return listenForMemberGroups(
+        userId,
+        (groups) => {
+          if (options?.withChatEnabled)
+            return setMemberGroups(
+              filterDefined(
+                groups.filter((group) => group.chatDisabled !== true)
+              )
+            )
+          return setMemberGroups(groups)
+        },
+        sort
+      )
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [options?.withChatEnabled, sort?.by, userId])
   return memberGroups
 }
 
@@ -88,19 +98,22 @@ export async function listMembers(group: Group, max?: number) {
   const { memberIds } = group
   const numToRetrieve = max ?? memberIds.length
   if (memberIds.length === 0) return []
-  if (numToRetrieve)
+  if (numToRetrieve > 100)
     return (await getUsers()).filter((user) =>
       group.memberIds.includes(user.id)
     )
   return await Promise.all(group.memberIds.slice(0, numToRetrieve).map(getUser))
 }
 
-export const useGroupsWithContract = (contractId: string | undefined) => {
-  const [groups, setGroups] = useState<Group[] | null | undefined>()
+export const useGroupsWithContract = (contract: Contract) => {
+  const [groups, setGroups] = useState<Group[]>([])
 
   useEffect(() => {
-    if (contractId) getGroupsWithContractId(contractId, setGroups)
-  }, [contractId])
+    if (contract.groupSlugs)
+      listGroups(uniq(contract.groupSlugs)).then((groups) =>
+        setGroups(filterDefined(groups))
+      )
+  }, [contract.groupSlugs])
 
   return groups
 }
diff --git a/web/hooks/use-notifications.ts b/web/hooks/use-notifications.ts
index f5502b85..a3ddeb29 100644
--- a/web/hooks/use-notifications.ts
+++ b/web/hooks/use-notifications.ts
@@ -1,4 +1,4 @@
-import { useEffect, useState } from 'react'
+import { useEffect, useMemo, useState } from 'react'
 import { notification_subscribe_types, PrivateUser } from 'common/user'
 import { Notification } from 'common/notification'
 import {
@@ -6,7 +6,7 @@ import {
   listenForNotifications,
 } from 'web/lib/firebase/notifications'
 import { groupBy, map } from 'lodash'
-import { useFirestoreQuery } from '@react-query-firebase/firestore'
+import { useFirestoreQueryData } from '@react-query-firebase/firestore'
 import { NOTIFICATIONS_PER_PAGE } from 'web/pages/notifications'
 
 export type NotificationGroup = {
@@ -19,36 +19,33 @@ export type NotificationGroup = {
 
 // For some reason react-query subscriptions don't actually listen for notifications
 // Use useUnseenPreferredNotificationGroups to listen for new notifications
-export function usePreferredGroupedNotifications(privateUser: PrivateUser) {
-  const [notificationGroups, setNotificationGroups] = useState<
-    NotificationGroup[] | undefined
-  >(undefined)
-  const [notifications, setNotifications] = useState<Notification[]>([])
-  const key = `notifications-${privateUser.id}-all`
+export function usePreferredGroupedNotifications(
+  privateUser: PrivateUser,
+  cachedNotifications?: Notification[]
+) {
+  const result = useFirestoreQueryData(
+    ['notifications-all', privateUser.id],
+    getNotificationsQuery(privateUser.id)
+  )
+  const notifications = useMemo(() => {
+    if (result.isLoading) return cachedNotifications ?? []
+    if (!result.data) return cachedNotifications ?? []
+    const notifications = result.data as Notification[]
 
-  const result = useFirestoreQuery([key], getNotificationsQuery(privateUser.id))
-  useEffect(() => {
-    if (result.isLoading) return
-    if (!result.data) return setNotifications([])
-    const notifications = result.data.docs.map(
-      (doc) => doc.data() as Notification
-    )
-
-    const notificationsToShow = getAppropriateNotifications(
+    return getAppropriateNotifications(
       notifications,
       privateUser.notificationPreferences
     ).filter((n) => !n.isSeenOnHref)
-    setNotifications(notificationsToShow)
-  }, [privateUser.notificationPreferences, result.data, result.isLoading])
+  }, [
+    cachedNotifications,
+    privateUser.notificationPreferences,
+    result.data,
+    result.isLoading,
+  ])
 
-  useEffect(() => {
-    if (!notifications) return
-
-    const groupedNotifications = groupNotifications(notifications)
-    setNotificationGroups(groupedNotifications)
+  return useMemo(() => {
+    if (notifications) return groupNotifications(notifications)
   }, [notifications])
-
-  return notificationGroups
 }
 
 export function useUnseenPreferredNotificationGroups(privateUser: PrivateUser) {
diff --git a/web/hooks/use-ping.ts b/web/hooks/use-ping.ts
new file mode 100644
index 00000000..31daa770
--- /dev/null
+++ b/web/hooks/use-ping.ts
@@ -0,0 +1,16 @@
+import { useEffect } from 'react'
+import { updateUser } from 'web/lib/firebase/users'
+
+export const usePing = (userId: string | undefined) => {
+  useEffect(() => {
+    if (!userId) return
+
+    const pingInterval = setInterval(() => {
+      updateUser(userId, {
+        lastPingTime: Date.now(),
+      })
+    }, 1000 * 30)
+
+    return () => clearInterval(pingInterval)
+  }, [userId])
+}
diff --git a/web/hooks/use-save-referral.ts b/web/hooks/use-save-referral.ts
new file mode 100644
index 00000000..788268b0
--- /dev/null
+++ b/web/hooks/use-save-referral.ts
@@ -0,0 +1,27 @@
+import { useRouter } from 'next/router'
+import { useEffect } from 'react'
+
+import { User, writeReferralInfo } from 'web/lib/firebase/users'
+
+export const useSaveReferral = (
+  user?: User | null,
+  options?: {
+    defaultReferrer?: string
+    contractId?: string
+    groupId?: string
+  }
+) => {
+  const router = useRouter()
+
+  useEffect(() => {
+    const { referrer } = router.query as {
+      referrer?: string
+    }
+
+    const actualReferrer = referrer || options?.defaultReferrer
+
+    if (!user && router.isReady && actualReferrer) {
+      writeReferralInfo(actualReferrer, options?.contractId, options?.groupId)
+    }
+  }, [user, router, options])
+}
diff --git a/web/hooks/use-seen-contracts.ts b/web/hooks/use-seen-contracts.ts
index 501e7b0c..d21ca84c 100644
--- a/web/hooks/use-seen-contracts.ts
+++ b/web/hooks/use-seen-contracts.ts
@@ -3,6 +3,7 @@ import { useEffect, useState } from 'react'
 import { Contract } from 'common/contract'
 import { trackView } from 'web/lib/firebase/tracking'
 import { useIsVisible } from './use-is-visible'
+import { useUser } from './use-user'
 
 export const useSeenContracts = () => {
   const [seenContracts, setSeenContracts] = useState<{
@@ -21,18 +22,19 @@ export const useSaveSeenContract = (
   contract: Contract
 ) => {
   const isVisible = useIsVisible(elem)
+  const user = useUser()
 
   useEffect(() => {
-    if (isVisible) {
+    if (isVisible && user) {
       const newSeenContracts = {
         ...getSeenContracts(),
         [contract.id]: Date.now(),
       }
       localStorage.setItem(key, JSON.stringify(newSeenContracts))
 
-      trackView(contract.id)
+      trackView(user.id, contract.id)
     }
-  }, [isVisible, contract])
+  }, [isVisible, user, contract])
 }
 
 const key = 'feed-seen-contracts'
diff --git a/web/hooks/use-sort-and-query-params.tsx b/web/hooks/use-sort-and-query-params.tsx
index a2590249..9023dc1a 100644
--- a/web/hooks/use-sort-and-query-params.tsx
+++ b/web/hooks/use-sort-and-query-params.tsx
@@ -3,6 +3,7 @@ import { useRouter } from 'next/router'
 import { useEffect, useMemo, useState } from 'react'
 import { useSearchBox } from 'react-instantsearch-hooks-web'
 import { track } from 'web/lib/service/analytics'
+import { DEFAULT_SORT } from 'web/components/contract-search'
 
 const MARKETS_SORT = 'markets_sort'
 
@@ -10,16 +11,11 @@ export type Sort =
   | 'newest'
   | 'oldest'
   | 'most-traded'
-  | 'most-popular'
   | '24-hour-vol'
   | 'close-date'
   | 'resolve-date'
   | 'last-updated'
-
-export function checkAgainstQuery(query: string, corpus: string) {
-  const queryWords = query.toLowerCase().split(' ')
-  return queryWords.every((word) => corpus.toLowerCase().includes(word))
-}
+  | 'score'
 
 export function getSavedSort() {
   // TODO: this obviously doesn't work with SSR, common sense would suggest
@@ -36,7 +32,7 @@ export function useInitialQueryAndSort(options?: {
   shouldLoadFromStorage?: boolean
 }) {
   const { defaultSort, shouldLoadFromStorage } = defaults(options, {
-    defaultSort: 'most-popular',
+    defaultSort: DEFAULT_SORT,
     shouldLoadFromStorage: true,
   })
   const router = useRouter()
@@ -58,9 +54,12 @@ export function useInitialQueryAndSort(options?: {
         console.log('ready loading from storage ', sort ?? defaultSort)
         const localSort = getSavedSort()
         if (localSort) {
-          router.query.s = localSort
           // Use replace to not break navigating back.
-          router.replace(router, undefined, { shallow: true })
+          router.replace(
+            { query: { ...router.query, s: localSort } },
+            undefined,
+            { shallow: true }
+          )
         }
         setInitialSort(localSort ?? defaultSort)
       } else {
@@ -84,7 +83,9 @@ export function useUpdateQueryAndSort(props: {
   const setSort = (sort: Sort | undefined) => {
     if (sort !== router.query.s) {
       router.query.s = sort
-      router.push(router, undefined, { shallow: true })
+      router.replace({ query: { ...router.query, s: sort } }, undefined, {
+        shallow: true,
+      })
       if (shouldLoadFromStorage) {
         localStorage.setItem(MARKETS_SORT, sort || '')
       }
@@ -102,7 +103,9 @@ export function useUpdateQueryAndSort(props: {
         } else {
           delete router.query.q
         }
-        router.push(router, undefined, { shallow: true })
+        router.replace({ query: router.query }, undefined, {
+          shallow: true,
+        })
         track('search', { query })
       }, 500),
     [router]
diff --git a/web/hooks/use-user.ts b/web/hooks/use-user.ts
index e04a69ca..4c492d6c 100644
--- a/web/hooks/use-user.ts
+++ b/web/hooks/use-user.ts
@@ -1,4 +1,4 @@
-import { useEffect, useState } from 'react'
+import { useContext, useEffect, useState } from 'react'
 import { useFirestoreDocumentData } from '@react-query-firebase/firestore'
 import { QueryClient } from 'react-query'
 
@@ -6,32 +6,14 @@ import { doc, DocumentData } from 'firebase/firestore'
 import { PrivateUser } from 'common/user'
 import {
   getUser,
-  listenForLogin,
   listenForPrivateUser,
-  listenForUser,
   User,
   users,
 } from 'web/lib/firebase/users'
-import { useStateCheckEquality } from './use-state-check-equality'
-import { identifyUser, setUserProperty } from 'web/lib/service/analytics'
+import { AuthContext } from 'web/components/auth-context'
 
 export const useUser = () => {
-  const [user, setUser] = useStateCheckEquality<User | null | undefined>(
-    undefined
-  )
-
-  useEffect(() => listenForLogin(setUser), [setUser])
-
-  useEffect(() => {
-    if (user) {
-      identifyUser(user.id)
-      setUserProperty('username', user.username)
-
-      return listenForUser(user.id, setUser)
-    }
-  }, [user, setUser])
-
-  return user
+  return useContext(AuthContext)
 }
 
 export const usePrivateUser = (userId?: string) => {
diff --git a/web/hooks/use-users.ts b/web/hooks/use-users.ts
index 1312444e..659395b8 100644
--- a/web/hooks/use-users.ts
+++ b/web/hooks/use-users.ts
@@ -1,32 +1,28 @@
 import { useState, useEffect } from 'react'
 import { PrivateUser, User } from 'common/user'
-import {
-  listenForAllUsers,
-  listenForPrivateUsers,
-} from 'web/lib/firebase/users'
 import { groupBy, sortBy, difference } from 'lodash'
 import { getContractsOfUserBets } from 'web/lib/firebase/bets'
 import { useFollows } from './use-follows'
 import { useUser } from './use-user'
+import { useFirestoreQueryData } from '@react-query-firebase/firestore'
+import { DocumentData } from 'firebase/firestore'
+import { users, privateUsers } from 'web/lib/firebase/users'
 
 export const useUsers = () => {
-  const [users, setUsers] = useState<User[]>([])
-
-  useEffect(() => {
-    listenForAllUsers(setUsers)
-  }, [])
-
-  return users
+  const result = useFirestoreQueryData<DocumentData, User[]>(['users'], users, {
+    subscribe: true,
+    includeMetadataChanges: true,
+  })
+  return result.data ?? []
 }
 
 export const usePrivateUsers = () => {
-  const [users, setUsers] = useState<PrivateUser[]>([])
-
-  useEffect(() => {
-    listenForPrivateUsers(setUsers)
-  }, [])
-
-  return users
+  const result = useFirestoreQueryData<DocumentData, PrivateUser[]>(
+    ['private users'],
+    privateUsers,
+    { subscribe: true, includeMetadataChanges: true }
+  )
+  return result.data || []
 }
 
 export const useDiscoverUsers = (userId: string | null | undefined) => {
diff --git a/web/lib/firebase/auth.ts b/web/lib/firebase/auth.ts
new file mode 100644
index 00000000..b6daea6e
--- /dev/null
+++ b/web/lib/firebase/auth.ts
@@ -0,0 +1,54 @@
+import { PROJECT_ID } from 'common/envs/constants'
+import { setCookie, getCookies } from '../util/cookie'
+import { IncomingMessage, ServerResponse } from 'http'
+
+const TOKEN_KINDS = ['refresh', 'id'] as const
+type TokenKind = typeof TOKEN_KINDS[number]
+
+const getAuthCookieName = (kind: TokenKind) => {
+  const suffix = `${PROJECT_ID}_${kind}`.toUpperCase().replace(/-/g, '_')
+  return `FIREBASE_TOKEN_${suffix}`
+}
+
+const ID_COOKIE_NAME = getAuthCookieName('id')
+const REFRESH_COOKIE_NAME = getAuthCookieName('refresh')
+
+export const getAuthCookies = (request?: IncomingMessage) => {
+  const data = request != null ? request.headers.cookie ?? '' : document.cookie
+  const cookies = getCookies(data)
+  return {
+    idToken: cookies[ID_COOKIE_NAME] as string | undefined,
+    refreshToken: cookies[REFRESH_COOKIE_NAME] as string | undefined,
+  }
+}
+
+export const setAuthCookies = (
+  idToken?: string,
+  refreshToken?: string,
+  response?: ServerResponse
+) => {
+  // these tokens last an hour
+  const idMaxAge = idToken != null ? 60 * 60 : 0
+  const idCookie = setCookie(ID_COOKIE_NAME, idToken ?? '', [
+    ['path', '/'],
+    ['max-age', idMaxAge.toString()],
+    ['samesite', 'lax'],
+    ['secure'],
+  ])
+  // these tokens don't expire
+  const refreshMaxAge = refreshToken != null ? 60 * 60 * 24 * 365 * 10 : 0
+  const refreshCookie = setCookie(REFRESH_COOKIE_NAME, refreshToken ?? '', [
+    ['path', '/'],
+    ['max-age', refreshMaxAge.toString()],
+    ['samesite', 'lax'],
+    ['secure'],
+  ])
+  if (response != null) {
+    response.setHeader('Set-Cookie', [idCookie, refreshCookie])
+  } else {
+    document.cookie = idCookie
+    document.cookie = refreshCookie
+  }
+}
+
+export const deleteAuthCookies = () => setAuthCookies()
diff --git a/web/lib/firebase/contracts.ts b/web/lib/firebase/contracts.ts
index 63efa53b..14594803 100644
--- a/web/lib/firebase/contracts.ts
+++ b/web/lib/firebase/contracts.ts
@@ -1,17 +1,17 @@
 import dayjs from 'dayjs'
 import {
-  doc,
-  setDoc,
-  deleteDoc,
-  where,
   collection,
-  query,
-  getDocs,
-  orderBy,
+  deleteDoc,
+  doc,
   getDoc,
-  updateDoc,
+  getDocs,
   limit,
+  orderBy,
+  query,
+  setDoc,
   startAfter,
+  updateDoc,
+  where,
 } from 'firebase/firestore'
 import { sortBy, sum } from 'lodash'
 
@@ -129,6 +129,7 @@ export async function listContractsByGroupSlug(
 ): Promise<Contract[]> {
   const q = query(contracts, where('groupSlugs', 'array-contains', slug))
   const snapshot = await getDocs(q)
+  console.log(snapshot.docs.map((doc) => doc.data()))
   return snapshot.docs.map((doc) => doc.data())
 }
 
diff --git a/web/lib/firebase/groups.ts b/web/lib/firebase/groups.ts
index 6d695b7f..151e7fa1 100644
--- a/web/lib/firebase/groups.ts
+++ b/web/lib/firebase/groups.ts
@@ -7,7 +7,7 @@ import {
   where,
 } from 'firebase/firestore'
 import { sortBy, uniq } from 'lodash'
-import { Group } from 'common/group'
+import { Group, GROUP_CHAT_SLUG, GroupLink } from 'common/group'
 import { updateContract } from './contracts'
 import {
   coll,
@@ -22,7 +22,12 @@ export const groups = coll<Group>('groups')
 
 export function groupPath(
   groupSlug: string,
-  subpath?: 'edit' | 'questions' | 'about' | 'chat' | 'rankings'
+  subpath?:
+    | 'edit'
+    | 'markets'
+    | 'about'
+    | typeof GROUP_CHAT_SLUG
+    | 'leaderboards'
 ) {
   return `/group/${groupSlug}${subpath ? `/${subpath}` : ''}`
 }
@@ -39,6 +44,10 @@ export async function listAllGroups() {
   return getValues<Group>(groups)
 }
 
+export async function listGroups(groupSlugs: string[]) {
+  return Promise.all(groupSlugs.map(getGroupBySlug))
+}
+
 export function listenForGroups(setGroups: (groups: Group[]) => void) {
   return listenForValues(groups, setGroups)
 }
@@ -62,29 +71,38 @@ export function listenForGroup(
 
 export function listenForMemberGroups(
   userId: string,
-  setGroups: (groups: Group[]) => void
+  setGroups: (groups: Group[]) => void,
+  sort?: { by: 'mostRecentChatActivityTime' | 'mostRecentContractAddedTime' }
 ) {
   const q = query(groups, where('memberIds', 'array-contains', userId))
-
+  const sorter = (group: Group) => {
+    if (sort?.by === 'mostRecentChatActivityTime') {
+      return group.mostRecentChatActivityTime ?? group.createdTime
+    }
+    if (sort?.by === 'mostRecentContractAddedTime') {
+      return group.mostRecentContractAddedTime ?? group.createdTime
+    }
+    return group.mostRecentActivityTime
+  }
   return listenForValues<Group>(q, (groups) => {
-    const sorted = sortBy(groups, [(group) => -group.mostRecentActivityTime])
+    const sorted = sortBy(groups, [(group) => -sorter(group)])
     setGroups(sorted)
   })
 }
 
-export async function getGroupsWithContractId(
+export async function listenForGroupsWithContractId(
   contractId: string,
   setGroups: (groups: Group[]) => void
 ) {
   const q = query(groups, where('contractIds', 'array-contains', contractId))
-  setGroups(await getValues<Group>(q))
+  return listenForValues<Group>(q, setGroups)
 }
 
-export async function addUserToGroupViaSlug(groupSlug: string, userId: string) {
+export async function addUserToGroupViaId(groupId: string, userId: string) {
   // get group to get the member ids
-  const group = await getGroupBySlug(groupSlug)
+  const group = await getGroup(groupId)
   if (!group) {
-    console.error(`Group not found: ${groupSlug}`)
+    console.error(`Group not found: ${groupId}`)
     return
   }
   return await joinGroup(group, userId)
@@ -106,9 +124,27 @@ export async function leaveGroup(group: Group, userId: string): Promise<void> {
   return await updateGroup(group, { memberIds: uniq(newMemberIds) })
 }
 
-export async function addContractToGroup(group: Group, contract: Contract) {
+export async function addContractToGroup(
+  group: Group,
+  contract: Contract,
+  userId: string
+) {
+  if (contract.groupLinks?.map((l) => l.groupId).includes(group.id)) return // already in that group
+
+  const newGroupLinks = [
+    ...(contract.groupLinks ?? []),
+    {
+      groupId: group.id,
+      createdTime: Date.now(),
+      slug: group.slug,
+      userId,
+      name: group.name,
+    } as GroupLink,
+  ]
+
   await updateContract(contract.id, {
-    groupSlugs: [...(contract.groupSlugs ?? []), group.slug],
+    groupSlugs: uniq([...(contract.groupSlugs ?? []), group.slug]),
+    groupLinks: newGroupLinks,
   })
   return await updateGroup(group, {
     contractIds: uniq([...group.contractIds, contract.id]),
@@ -120,8 +156,47 @@ export async function addContractToGroup(group: Group, contract: Contract) {
     })
 }
 
-export async function setContractGroupSlugs(group: Group, contractId: string) {
-  await updateContract(contractId, { groupSlugs: [group.slug] })
+export async function removeContractFromGroup(
+  group: Group,
+  contract: Contract
+) {
+  if (!contract.groupLinks?.map((l) => l.groupId).includes(group.id)) return // not in that group
+
+  const newGroupLinks = contract.groupLinks?.filter(
+    (link) => link.slug !== group.slug
+  )
+  await updateContract(contract.id, {
+    groupSlugs:
+      contract.groupSlugs?.filter((slug) => slug !== group.slug) ?? [],
+    groupLinks: newGroupLinks ?? [],
+  })
+  const newContractIds = group.contractIds.filter((id) => id !== contract.id)
+  return await updateGroup(group, {
+    contractIds: uniq(newContractIds),
+  })
+    .then(() => group)
+    .catch((err) => {
+      console.error('error removing contract from group', err)
+      return err
+    })
+}
+
+export async function setContractGroupLinks(
+  group: Group,
+  contractId: string,
+  userId: string
+) {
+  await updateContract(contractId, {
+    groupLinks: [
+      {
+        groupId: group.id,
+        name: group.name,
+        slug: group.slug,
+        userId,
+        createdTime: Date.now(),
+      } as GroupLink,
+    ],
+  })
   return await updateGroup(group, {
     contractIds: uniq([...group.contractIds, contractId]),
   })
diff --git a/web/lib/firebase/server-auth.ts b/web/lib/firebase/server-auth.ts
new file mode 100644
index 00000000..47eadb45
--- /dev/null
+++ b/web/lib/firebase/server-auth.ts
@@ -0,0 +1,93 @@
+import * as admin from 'firebase-admin'
+import fetch from 'node-fetch'
+import { IncomingMessage, ServerResponse } from 'http'
+import { FIREBASE_CONFIG, PROJECT_ID } from 'common/envs/constants'
+import { getAuthCookies, setAuthCookies } from './auth'
+import { GetServerSideProps, GetServerSidePropsContext } from 'next'
+
+const ensureApp = async () => {
+  // Note: firebase-admin can only be imported from a server context,
+  // because it relies on Node standard library dependencies.
+  if (admin.apps.length === 0) {
+    // never initialize twice
+    return admin.initializeApp({ projectId: PROJECT_ID })
+  }
+  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+  return admin.apps[0]!
+}
+
+const requestFirebaseIdToken = async (refreshToken: string) => {
+  // See https://firebase.google.com/docs/reference/rest/auth/#section-refresh-token
+  const refreshUrl = new URL('https://securetoken.googleapis.com/v1/token')
+  refreshUrl.searchParams.append('key', FIREBASE_CONFIG.apiKey)
+  const result = await fetch(refreshUrl.toString(), {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/x-www-form-urlencoded',
+    },
+    body: new URLSearchParams({
+      grant_type: 'refresh_token',
+      refresh_token: refreshToken,
+    }),
+  })
+  if (!result.ok) {
+    throw new Error(`Could not refresh ID token: ${await result.text()}`)
+  }
+  return (await result.json()) as any
+}
+
+type RequestContext = {
+  req: IncomingMessage
+  res: ServerResponse
+}
+
+export const getServerAuthenticatedUid = async (ctx: RequestContext) => {
+  const app = await ensureApp()
+  const auth = app.auth()
+  const { idToken, refreshToken } = getAuthCookies(ctx.req)
+
+  // If we have a valid ID token, verify the user immediately with no network trips.
+  // If the ID token doesn't verify, we'll have to refresh it to see who they are.
+  // If they don't have any tokens, then we have no idea who they are.
+  if (idToken != null) {
+    try {
+      return (await auth.verifyIdToken(idToken))?.uid
+    } catch {
+      // plausibly expired; try the refresh token, if it's present
+    }
+  }
+  if (refreshToken != null) {
+    try {
+      const resp = await requestFirebaseIdToken(refreshToken)
+      setAuthCookies(resp.id_token, resp.refresh_token, ctx.res)
+      return (await auth.verifyIdToken(resp.id_token))?.uid
+    } catch (e) {
+      // this is a big unexpected problem -- either their cookies are corrupt
+      // or the refresh token API is down. functionally, they are not logged in
+      console.error(e)
+    }
+  }
+  return undefined
+}
+
+export const redirectIfLoggedIn = (dest: string, fn?: GetServerSideProps) => {
+  return async (ctx: GetServerSidePropsContext) => {
+    const uid = await getServerAuthenticatedUid(ctx)
+    if (uid == null) {
+      return fn != null ? await fn(ctx) : { props: {} }
+    } else {
+      return { redirect: { destination: dest, permanent: false } }
+    }
+  }
+}
+
+export const redirectIfLoggedOut = (dest: string, fn?: GetServerSideProps) => {
+  return async (ctx: GetServerSidePropsContext) => {
+    const uid = await getServerAuthenticatedUid(ctx)
+    if (uid == null) {
+      return { redirect: { destination: dest, permanent: false } }
+    } else {
+      return fn != null ? await fn(ctx) : { props: {} }
+    }
+  }
+}
diff --git a/web/lib/firebase/storage.ts b/web/lib/firebase/storage.ts
index 2fc2ccc7..4918a99c 100644
--- a/web/lib/firebase/storage.ts
+++ b/web/lib/firebase/storage.ts
@@ -1,4 +1,5 @@
 import { ref, uploadBytesResumable, getDownloadURL } from 'firebase/storage'
+import { nanoid } from 'nanoid'
 import { storage } from './init'
 
 // TODO: compress large images
@@ -7,7 +8,10 @@ export const uploadImage = async (
   file: File,
   onProgress?: (progress: number, isRunning: boolean) => void
 ) => {
-  const storageRef = ref(storage, `user-images/${username}/${file.name}`)
+  // Replace filename with a nanoid to avoid collisions
+  const [, ext] = file.name.split('.')
+  const filename = `${nanoid(10)}.${ext}`
+  const storageRef = ref(storage, `user-images/${username}/${filename}`)
   const uploadTask = uploadBytesResumable(storageRef, file)
 
   let resolvePromise: (url: string) => void
diff --git a/web/lib/firebase/tracking.ts b/web/lib/firebase/tracking.ts
index f6ad3aa8..d1828e01 100644
--- a/web/lib/firebase/tracking.ts
+++ b/web/lib/firebase/tracking.ts
@@ -2,16 +2,9 @@ import { doc, collection, setDoc } from 'firebase/firestore'
 
 import { db } from './init'
 import { ClickEvent, LatencyEvent, View } from 'common/tracking'
-import { listenForLogin, User } from './users'
 
-let user: User | null = null
-if (typeof window !== 'undefined') {
-  listenForLogin((u) => (user = u))
-}
-
-export async function trackView(contractId: string) {
-  if (!user) return
-  const ref = doc(collection(db, 'private-users', user.id, 'views'))
+export async function trackView(userId: string, contractId: string) {
+  const ref = doc(collection(db, 'private-users', userId, 'views'))
 
   const view: View = {
     contractId,
@@ -21,9 +14,8 @@ export async function trackView(contractId: string) {
   return await setDoc(ref, view)
 }
 
-export async function trackClick(contractId: string) {
-  if (!user) return
-  const ref = doc(collection(db, 'private-users', user.id, 'events'))
+export async function trackClick(userId: string, contractId: string) {
+  const ref = doc(collection(db, 'private-users', userId, 'events'))
 
   const clickEvent: ClickEvent = {
     type: 'click',
@@ -35,11 +27,11 @@ export async function trackClick(contractId: string) {
 }
 
 export async function trackLatency(
+  userId: string,
   type: 'feed' | 'portfolio',
   latency: number
 ) {
-  if (!user) return
-  const ref = doc(collection(db, 'private-users', user.id, 'latency'))
+  const ref = doc(collection(db, 'private-users', userId, 'latency'))
 
   const latencyEvent: LatencyEvent = {
     type,
diff --git a/web/lib/firebase/users.ts b/web/lib/firebase/users.ts
index 29cc9266..4f618586 100644
--- a/web/lib/firebase/users.ts
+++ b/web/lib/firebase/users.ts
@@ -15,15 +15,10 @@ import {
 } from 'firebase/firestore'
 import { getAuth } from 'firebase/auth'
 import { ref, getStorage, uploadBytes, getDownloadURL } from 'firebase/storage'
-import {
-  onAuthStateChanged,
-  GoogleAuthProvider,
-  signInWithPopup,
-} from 'firebase/auth'
+import { GoogleAuthProvider, signInWithPopup } from 'firebase/auth'
 import { zip } from 'lodash'
 import { app, db } from './init'
 import { PortfolioMetrics, PrivateUser, User } from 'common/user'
-import { createUser } from './api'
 import {
   coll,
   getValue,
@@ -35,9 +30,8 @@ import { feed } from 'common/feed'
 import { CATEGORY_LIST } from 'common/categories'
 import { safeLocalStorage } from '../util/local'
 import { filterDefined } from 'common/util/array'
-import { addUserToGroupViaSlug } from 'web/lib/firebase/groups'
+import { addUserToGroupViaId } from 'web/lib/firebase/groups'
 import { removeUndefinedProps } from 'common/util/object'
-import { randomString } from 'common/util/random'
 import dayjs from 'dayjs'
 import utc from 'dayjs/plugin/utc'
 dayjs.extend(utc)
@@ -96,16 +90,15 @@ export function listenForPrivateUser(
   return listenForValue<PrivateUser>(userRef, setPrivateUser)
 }
 
-const CACHED_USER_KEY = 'CACHED_USER_KEY'
 const CACHED_REFERRAL_USERNAME_KEY = 'CACHED_REFERRAL_KEY'
 const CACHED_REFERRAL_CONTRACT_ID_KEY = 'CACHED_REFERRAL_CONTRACT_KEY'
-const CACHED_REFERRAL_GROUP_SLUG_KEY = 'CACHED_REFERRAL_GROUP_KEY'
+const CACHED_REFERRAL_GROUP_ID_KEY = 'CACHED_REFERRAL_GROUP_KEY'
 
 export function writeReferralInfo(
   defaultReferrerUsername: string,
   contractId?: string,
   referralUsername?: string,
-  groupSlug?: string
+  groupId?: string
 ) {
   const local = safeLocalStorage()
   const cachedReferralUser = local?.getItem(CACHED_REFERRAL_USERNAME_KEY)
@@ -121,7 +114,7 @@ export function writeReferralInfo(
     local?.setItem(CACHED_REFERRAL_USERNAME_KEY, referralUsername)
 
   // Always write the most recent explicit group invite query value
-  if (groupSlug) local?.setItem(CACHED_REFERRAL_GROUP_SLUG_KEY, groupSlug)
+  if (groupId) local?.setItem(CACHED_REFERRAL_GROUP_ID_KEY, groupId)
 
   // Write the first contract id that we see.
   const cachedReferralContract = local?.getItem(CACHED_REFERRAL_CONTRACT_ID_KEY)
@@ -129,19 +122,19 @@ export function writeReferralInfo(
     local?.setItem(CACHED_REFERRAL_CONTRACT_ID_KEY, contractId)
 }
 
-async function setCachedReferralInfoForUser(user: User | null) {
+export async function setCachedReferralInfoForUser(user: User | null) {
   if (!user || user.referredByUserId) return
   // if the user wasn't created in the last minute, don't bother
   const now = dayjs().utc()
   const userCreatedTime = dayjs(user.createdTime)
-  if (now.diff(userCreatedTime, 'minute') > 1) return
+  if (now.diff(userCreatedTime, 'minute') > 5) return
 
   const local = safeLocalStorage()
   const cachedReferralUsername = local?.getItem(CACHED_REFERRAL_USERNAME_KEY)
   const cachedReferralContractId = local?.getItem(
     CACHED_REFERRAL_CONTRACT_ID_KEY
   )
-  const cachedReferralGroupSlug = local?.getItem(CACHED_REFERRAL_GROUP_SLUG_KEY)
+  const cachedReferralGroupId = local?.getItem(CACHED_REFERRAL_GROUP_ID_KEY)
 
   // get user via username
   if (cachedReferralUsername)
@@ -155,6 +148,9 @@ async function setCachedReferralInfoForUser(user: User | null) {
           referredByContractId: cachedReferralContractId
             ? cachedReferralContractId
             : undefined,
+          referredByGroupId: cachedReferralGroupId
+            ? cachedReferralGroupId
+            : undefined,
         })
       )
         .catch((err) => {
@@ -165,65 +161,25 @@ async function setCachedReferralInfoForUser(user: User | null) {
             userId: user.id,
             referredByUserId: referredByUser.id,
             referredByContractId: cachedReferralContractId,
-            referredByGroupSlug: cachedReferralGroupSlug,
+            referredByGroupId: cachedReferralGroupId,
           })
         })
     })
 
-  if (cachedReferralGroupSlug)
-    addUserToGroupViaSlug(cachedReferralGroupSlug, user.id)
+  if (cachedReferralGroupId) addUserToGroupViaId(cachedReferralGroupId, user.id)
 
-  local?.removeItem(CACHED_REFERRAL_GROUP_SLUG_KEY)
+  local?.removeItem(CACHED_REFERRAL_GROUP_ID_KEY)
   local?.removeItem(CACHED_REFERRAL_USERNAME_KEY)
   local?.removeItem(CACHED_REFERRAL_CONTRACT_ID_KEY)
 }
 
-// used to avoid weird race condition
-let createUserPromise: Promise<User> | undefined = undefined
-
-export function listenForLogin(onUser: (user: User | null) => void) {
-  const local = safeLocalStorage()
-  const cachedUser = local?.getItem(CACHED_USER_KEY)
-  onUser(cachedUser && JSON.parse(cachedUser))
-
-  return onAuthStateChanged(auth, async (fbUser) => {
-    if (fbUser) {
-      let user: User | null = await getUser(fbUser.uid)
-
-      if (!user) {
-        if (createUserPromise == null) {
-          const local = safeLocalStorage()
-          let deviceToken = local?.getItem('device-token')
-          if (!deviceToken) {
-            deviceToken = randomString()
-            local?.setItem('device-token', deviceToken)
-          }
-          createUserPromise = createUser({ deviceToken }).then((r) => r as User)
-        }
-        user = await createUserPromise
-      }
-
-      onUser(user)
-
-      // Persist to local storage, to reduce login blink next time.
-      // Note: Cap on localStorage size is ~5mb
-      local?.setItem(CACHED_USER_KEY, JSON.stringify(user))
-      setCachedReferralInfoForUser(user)
-    } else {
-      // User logged out; reset to null
-      onUser(null)
-      local?.removeItem(CACHED_USER_KEY)
-    }
-  })
-}
-
 export async function firebaseLogin() {
   const provider = new GoogleAuthProvider()
   return signInWithPopup(auth, provider)
 }
 
 export async function firebaseLogout() {
-  auth.signOut()
+  await auth.signOut()
 }
 
 const storage = getStorage(app)
@@ -254,16 +210,6 @@ export async function listAllUsers() {
   return docs.map((doc) => doc.data())
 }
 
-export function listenForAllUsers(setUsers: (users: User[]) => void) {
-  listenForValues(users, setUsers)
-}
-
-export function listenForPrivateUsers(
-  setUsers: (users: PrivateUser[]) => void
-) {
-  listenForValues(privateUsers, setUsers)
-}
-
 export function getTopTraders(period: Period) {
   const topTraders = query(
     users,
@@ -271,7 +217,7 @@ export function getTopTraders(period: Period) {
     limit(20)
   )
 
-  return getValues(topTraders)
+  return getValues<User>(topTraders)
 }
 
 export function getTopCreators(period: Period) {
@@ -280,7 +226,7 @@ export function getTopCreators(period: Period) {
     orderBy('creatorVolumeCached.' + period, 'desc'),
     limit(20)
   )
-  return getValues(topCreators)
+  return getValues<User>(topCreators)
 }
 
 export async function getTopFollowed() {
diff --git a/web/lib/util/cookie.ts b/web/lib/util/cookie.ts
new file mode 100644
index 00000000..14999fd4
--- /dev/null
+++ b/web/lib/util/cookie.ts
@@ -0,0 +1,33 @@
+type CookieOptions = string[][]
+
+const encodeCookie = (name: string, val: string) => {
+  return `${name}=${encodeURIComponent(val)}`
+}
+
+const decodeCookie = (cookie: string) => {
+  const parts = cookie.trim().split('=')
+  if (parts.length < 2) {
+    throw new Error(`Invalid cookie contents: ${cookie}`)
+  }
+  const rest = parts.slice(1).join('') // there may be more = in the value
+  return [parts[0], decodeURIComponent(rest)] as const
+}
+
+export const setCookie = (name: string, val: string, opts?: CookieOptions) => {
+  const parts = [encodeCookie(name, val)]
+  if (opts != null) {
+    parts.push(...opts.map((opt) => opt.join('=')))
+  }
+  return parts.join('; ')
+}
+
+// Note that this intentionally ignores the case where multiple cookies have
+// the same name but different paths. Hopefully we never need to think about it.
+export const getCookies = (cookies: string) => {
+  const data = cookies.trim()
+  if (!data) {
+    return {}
+  } else {
+    return Object.fromEntries(data.split(';').map(decodeCookie))
+  }
+}
diff --git a/web/next.config.js b/web/next.config.js
index 56f643d3..37758952 100644
--- a/web/next.config.js
+++ b/web/next.config.js
@@ -4,6 +4,7 @@ const API_DOCS_URL = 'https://docs.manifold.markets/api'
 module.exports = {
   staticPageGenerationTimeout: 600, // e.g. stats page
   reactStrictMode: true,
+  optimizeFonts: false,
   experimental: {
     externalDir: true,
     optimizeCss: true,
diff --git a/web/package.json b/web/package.json
index 7393d4b8..279e381f 100644
--- a/web/package.json
+++ b/web/package.json
@@ -41,7 +41,7 @@
     "gridjs-react": "5.0.2",
     "lodash": "4.17.21",
     "nanoid": "^3.3.4",
-    "next": "12.1.2",
+    "next": "12.2.2",
     "node-fetch": "3.2.4",
     "react": "17.0.2",
     "react-confetti": "6.0.1",
diff --git a/web/pages/[username]/[contractSlug].tsx b/web/pages/[username]/[contractSlug].tsx
index 17453770..43dd0ad7 100644
--- a/web/pages/[username]/[contractSlug].tsx
+++ b/web/pages/[username]/[contractSlug].tsx
@@ -1,6 +1,5 @@
-import React, { useEffect, useMemo, useState } from 'react'
+import React, { useEffect, useState } from 'react'
 import { ArrowLeftIcon } from '@heroicons/react/outline'
-import { keyBy, sortBy, groupBy, sumBy, mapValues } from 'lodash'
 
 import { useContractWithPreload } from 'web/hooks/use-contract'
 import { ContractOverview } from 'web/components/contract/contract-overview'
@@ -8,9 +7,7 @@ import { BetPanel } from 'web/components/bet-panel'
 import { Col } from 'web/components/layout/col'
 import { useUser } from 'web/hooks/use-user'
 import { ResolutionPanel } from 'web/components/resolution-panel'
-import { Title } from 'web/components/title'
 import { Spacer } from 'web/components/layout/spacer'
-import { listUsers, User, writeReferralInfo } from 'web/lib/firebase/users'
 import {
   Contract,
   getContractFromSlug,
@@ -24,28 +21,26 @@ import { Comment, listAllComments } from 'web/lib/firebase/comments'
 import Custom404 from '../404'
 import { AnswersPanel } from 'web/components/answers/answers-panel'
 import { fromPropz, usePropz } from 'web/hooks/use-propz'
-import { Leaderboard } from 'web/components/leaderboard'
-import { resolvedPayout } from 'common/calculate'
-import { formatMoney } from 'common/util/format'
-import { useUserById } from 'web/hooks/use-user'
 import { ContractTabs } from 'web/components/contract/contract-tabs'
 import { contractTextDetails } from 'web/components/contract/contract-details'
 import { useWindowSize } from 'web/hooks/use-window-size'
 import Confetti from 'react-confetti'
 import { NumericBetPanel } from '../../components/numeric-bet-panel'
 import { NumericResolutionPanel } from '../../components/numeric-resolution-panel'
-import { FeedComment } from 'web/components/feed/feed-comments'
-import { FeedBet } from 'web/components/feed/feed-bets'
 import { useIsIframe } from 'web/hooks/use-is-iframe'
 import ContractEmbedPage from '../embed/[username]/[contractSlug]'
 import { useBets } from 'web/hooks/use-bets'
 import { CPMMBinaryContract } from 'common/contract'
 import { AlertBox } from 'web/components/alert-box'
 import { useTracking } from 'web/hooks/use-tracking'
-import { CommentTipMap, useTipTxns } from 'web/hooks/use-tip-txns'
-import { useRouter } from 'next/router'
+import { useTipTxns } from 'web/hooks/use-tip-txns'
 import { useLiquidity } from 'web/hooks/use-liquidity'
 import { richTextToString } from 'common/util/parse'
+import { useSaveReferral } from 'web/hooks/use-save-referral'
+import {
+  ContractLeaderboard,
+  ContractTopTrades,
+} from 'web/components/contract/contract-leaderboard'
 
 export const getStaticProps = fromPropz(getStaticPropz)
 export async function getStaticPropz(props: {
@@ -157,15 +152,10 @@ export function ContractPageContent(
 
   const ogCardProps = getOpenGraphProps(contract)
 
-  const router = useRouter()
-
-  useEffect(() => {
-    const { referrer } = router.query as {
-      referrer?: string
-    }
-    if (!user && router.isReady)
-      writeReferralInfo(contract.creatorUsername, contract.id, referrer)
-  }, [user, contract, router])
+  useSaveReferral(user, {
+    defaultReferrer: contract.creatorUsername,
+    contractId: contract.id,
+  })
 
   const rightSidebar = hasSidePanel ? (
     <Col className="gap-4">
@@ -267,129 +257,6 @@ export function ContractPageContent(
   )
 }
 
-function ContractLeaderboard(props: { contract: Contract; bets: Bet[] }) {
-  const { contract, bets } = props
-  const [users, setUsers] = useState<User[]>()
-
-  const { userProfits, top5Ids } = useMemo(() => {
-    // Create a map of userIds to total profits (including sales)
-    const openBets = bets.filter((bet) => !bet.isSold && !bet.sale)
-    const betsByUser = groupBy(openBets, 'userId')
-
-    const userProfits = mapValues(betsByUser, (bets) =>
-      sumBy(bets, (bet) => resolvedPayout(contract, bet) - bet.amount)
-    )
-    // Find the 5 users with the most profits
-    const top5Ids = Object.entries(userProfits)
-      .sort(([_i1, p1], [_i2, p2]) => p2 - p1)
-      .filter(([, p]) => p > 0)
-      .slice(0, 5)
-      .map(([id]) => id)
-    return { userProfits, top5Ids }
-  }, [contract, bets])
-
-  useEffect(() => {
-    if (top5Ids.length > 0) {
-      listUsers(top5Ids).then((users) => {
-        const sortedUsers = sortBy(users, (user) => -userProfits[user.id])
-        setUsers(sortedUsers)
-      })
-    }
-  }, [userProfits, top5Ids])
-
-  return users && users.length > 0 ? (
-    <Leaderboard
-      title="🏅 Top bettors"
-      users={users || []}
-      columns={[
-        {
-          header: 'Total profit',
-          renderCell: (user) => formatMoney(userProfits[user.id] || 0),
-        },
-      ]}
-      className="mt-12 max-w-sm"
-    />
-  ) : null
-}
-
-function ContractTopTrades(props: {
-  contract: Contract
-  bets: Bet[]
-  comments: Comment[]
-  tips: CommentTipMap
-}) {
-  const { contract, bets, comments, tips } = props
-  const commentsById = keyBy(comments, 'id')
-  const betsById = keyBy(bets, 'id')
-
-  // If 'id2' is the sale of 'id1', both are logged with (id2 - id1) of profit
-  // Otherwise, we record the profit at resolution time
-  const profitById: Record<string, number> = {}
-  for (const bet of bets) {
-    if (bet.sale) {
-      const originalBet = betsById[bet.sale.betId]
-      const profit = bet.sale.amount - originalBet.amount
-      profitById[bet.id] = profit
-      profitById[originalBet.id] = profit
-    } else {
-      profitById[bet.id] = resolvedPayout(contract, bet) - bet.amount
-    }
-  }
-
-  // Now find the betId with the highest profit
-  const topBetId = sortBy(bets, (b) => -profitById[b.id])[0]?.id
-  const topBettor = useUserById(betsById[topBetId]?.userId)
-
-  // And also the commentId of the comment with the highest profit
-  const topCommentId = sortBy(
-    comments,
-    (c) => c.betId && -profitById[c.betId]
-  )[0]?.id
-
-  return (
-    <div className="mt-12 max-w-sm">
-      {topCommentId && profitById[topCommentId] > 0 && (
-        <>
-          <Title text="💬 Proven correct" className="!mt-0" />
-          <div className="relative flex items-start space-x-3 rounded-md bg-gray-50 px-2 py-4">
-            <FeedComment
-              contract={contract}
-              comment={commentsById[topCommentId]}
-              tips={tips[topCommentId]}
-              betsBySameUser={[betsById[topCommentId]]}
-              truncate={false}
-              smallAvatar={false}
-            />
-          </div>
-          <div className="mt-2 text-sm text-gray-500">
-            {commentsById[topCommentId].userName} made{' '}
-            {formatMoney(profitById[topCommentId] || 0)}!
-          </div>
-          <Spacer h={16} />
-        </>
-      )}
-
-      {/* If they're the same, only show the comment; otherwise show both */}
-      {topBettor && topBetId !== topCommentId && profitById[topBetId] > 0 && (
-        <>
-          <Title text="💸 Smartest money" className="!mt-0" />
-          <div className="relative flex items-start space-x-3 rounded-md bg-gray-50 px-2 py-4">
-            <FeedBet
-              contract={contract}
-              bet={betsById[topBetId]}
-              hideOutcome={false}
-              smallAvatar={false}
-            />
-          </div>
-          <div className="mt-2 text-sm text-gray-500">
-            {topBettor?.name} made {formatMoney(profitById[topBetId] || 0)}!
-          </div>
-        </>
-      )}
-    </div>
-  )
-}
-
 const getOpenGraphProps = (contract: Contract) => {
   const {
     resolution,
diff --git a/web/pages/_app.tsx b/web/pages/_app.tsx
index d081bc9a..52316eb0 100644
--- a/web/pages/_app.tsx
+++ b/web/pages/_app.tsx
@@ -5,6 +5,7 @@ import Head from 'next/head'
 import Script from 'next/script'
 import { usePreserveScroll } from 'web/hooks/use-preserve-scroll'
 import { QueryClient, QueryClientProvider } from 'react-query'
+import { AuthProvider } from 'web/components/auth-context'
 
 function firstLine(msg: string) {
   return msg.replace(/\r?\n.*/s, '')
@@ -78,9 +79,11 @@ function MyApp({ Component, pageProps }: AppProps) {
         />
       </Head>
 
-      <QueryClientProvider client={queryClient}>
-        <Component {...pageProps} />
-      </QueryClientProvider>
+      <AuthProvider>
+        <QueryClientProvider client={queryClient}>
+          <Component {...pageProps} />
+        </QueryClientProvider>
+      </AuthProvider>
     </>
   )
 }
diff --git a/web/pages/_document.tsx b/web/pages/_document.tsx
index f1a7ccab..b8cb657c 100644
--- a/web/pages/_document.tsx
+++ b/web/pages/_document.tsx
@@ -6,16 +6,15 @@ export default function Document() {
     <Html data-theme="mantic" className="min-h-screen">
       <Head>
         <link rel="icon" href={ENV_CONFIG.faviconPath} />
-
-        <link rel="preconnect" href="https://fonts.googleapis.com" />
         <link
           rel="preconnect"
           href="https://fonts.gstatic.com"
-          crossOrigin="true"
+          crossOrigin="anonymous"
         />
         <link
-          href="https://fonts.googleapis.com/css2?family=Major+Mono+Display&family=Readex+Pro:wght@400;600;700&display=swap"
+          href="https://fonts.googleapis.com/css2?family=Major+Mono+Display&family=Readex+Pro:wght@300;400;600;700&display=swap"
           rel="stylesheet"
+          crossOrigin="anonymous"
         />
         <link
           rel="stylesheet"
@@ -24,7 +23,6 @@ export default function Document() {
           crossOrigin="anonymous"
         />
       </Head>
-
       <body className="font-readex-pro bg-base-200 min-h-screen">
         <Main />
         <NextScript />
diff --git a/web/pages/add-funds.tsx b/web/pages/add-funds.tsx
index f680d47b..ed25a21a 100644
--- a/web/pages/add-funds.tsx
+++ b/web/pages/add-funds.tsx
@@ -8,6 +8,9 @@ import { checkoutURL } from 'web/lib/service/stripe'
 import { Page } from 'web/components/page'
 import { useTracking } from 'web/hooks/use-tracking'
 import { trackCallback } from 'web/lib/service/analytics'
+import { redirectIfLoggedOut } from 'web/lib/firebase/server-auth'
+
+export const getServerSideProps = redirectIfLoggedOut('/')
 
 export default function AddFundsPage() {
   const user = useUser()
diff --git a/web/pages/admin.tsx b/web/pages/admin.tsx
index e709e875..81f23ba9 100644
--- a/web/pages/admin.tsx
+++ b/web/pages/admin.tsx
@@ -9,6 +9,9 @@ import { useContracts } from 'web/hooks/use-contracts'
 import { mapKeys } from 'lodash'
 import { useAdmin } from 'web/hooks/use-admin'
 import { contractPath } from 'web/lib/firebase/contracts'
+import { redirectIfLoggedOut } from 'web/lib/firebase/server-auth'
+
+export const getServerSideProps = redirectIfLoggedOut('/')
 
 function avatarHtml(avatarUrl: string) {
   return `<img
diff --git a/web/pages/api/v0/user/[username]/index.ts b/web/pages/api/v0/user/[username]/index.ts
new file mode 100644
index 00000000..58daffcd
--- /dev/null
+++ b/web/pages/api/v0/user/[username]/index.ts
@@ -0,0 +1,19 @@
+import { NextApiRequest, NextApiResponse } from 'next'
+import { getUserByUsername } from 'web/lib/firebase/users'
+import { applyCorsHeaders, CORS_UNRESTRICTED } from 'web/lib/api/cors'
+import { LiteUser, ApiError, toLiteUser } from '../../_types'
+
+export default async function handler(
+  req: NextApiRequest,
+  res: NextApiResponse<LiteUser | ApiError>
+) {
+  await applyCorsHeaders(req, res, CORS_UNRESTRICTED)
+  const { username } = req.query
+  const user = await getUserByUsername(username as string)
+  if (!user) {
+    res.status(404).json({ error: 'User not found' })
+    return
+  }
+  res.setHeader('Cache-Control', 'no-cache')
+  return res.status(200).json(toLiteUser(user))
+}
diff --git a/web/pages/api/v0/user/by-id/[id].ts b/web/pages/api/v0/user/by-id/[id].ts
new file mode 100644
index 00000000..6ed67d1c
--- /dev/null
+++ b/web/pages/api/v0/user/by-id/[id].ts
@@ -0,0 +1,19 @@
+import { NextApiRequest, NextApiResponse } from 'next'
+import { getUser } from 'web/lib/firebase/users'
+import { applyCorsHeaders, CORS_UNRESTRICTED } from 'web/lib/api/cors'
+import { LiteUser, ApiError, toLiteUser } from '../../_types'
+
+export default async function handler(
+  req: NextApiRequest,
+  res: NextApiResponse<LiteUser | ApiError>
+) {
+  await applyCorsHeaders(req, res, CORS_UNRESTRICTED)
+  const { id } = req.query
+  const user = await getUser(id as string)
+  if (!user) {
+    res.status(404).json({ error: 'User not found' })
+    return
+  }
+  res.setHeader('Cache-Control', 'no-cache')
+  return res.status(200).json(toLiteUser(user))
+}
diff --git a/web/pages/charity/[charitySlug].tsx b/web/pages/charity/[charitySlug].tsx
index 2cefa13b..89d2d3a3 100644
--- a/web/pages/charity/[charitySlug].tsx
+++ b/web/pages/charity/[charitySlug].tsx
@@ -1,6 +1,9 @@
 import { sortBy, sumBy, uniqBy } from 'lodash'
 import clsx from 'clsx'
 import React, { useEffect, useRef, useState } from 'react'
+import Image from 'next/image'
+import Confetti from 'react-confetti'
+
 import { Col } from 'web/components/layout/col'
 import { Row } from 'web/components/layout/row'
 import { Page } from 'web/components/page'
@@ -16,11 +19,10 @@ import { useRouter } from 'next/router'
 import Custom404 from '../404'
 import { useCharityTxns } from 'web/hooks/use-charity-txns'
 import { useWindowSize } from 'web/hooks/use-window-size'
-import Confetti from 'react-confetti'
 import { Donation } from 'web/components/charity/feed-items'
-import Image from 'next/image'
-import { manaToUSD } from '../../../common/util/format'
+import { manaToUSD } from 'common/util/format'
 import { track } from 'web/lib/service/analytics'
+import { SEO } from 'web/components/SEO'
 
 export default function CharityPageWrapper() {
   const router = useRouter()
@@ -63,6 +65,7 @@ function CharityPage(props: { charity: Charity }) {
         />
       }
     >
+      <SEO title={name} description={description} url="/groups" />
       {showConfetti && (
         <Confetti
           width={width ? width : 500}
diff --git a/web/pages/charity/index.tsx b/web/pages/charity/index.tsx
index 46201c3d..80003c81 100644
--- a/web/pages/charity/index.tsx
+++ b/web/pages/charity/index.tsx
@@ -13,13 +13,17 @@ import { CharityCard } from 'web/components/charity/charity-card'
 import { Col } from 'web/components/layout/col'
 import { Spacer } from 'web/components/layout/spacer'
 import { Page } from 'web/components/page'
-import { SiteLink } from 'web/components/site-link'
 import { Title } from 'web/components/title'
 import { getAllCharityTxns } from 'web/lib/firebase/txns'
 import { manaToUSD } from 'common/util/format'
 import { quadraticMatches } from 'common/quadratic-funding'
 import { Txn } from 'common/txn'
 import { useTracking } from 'web/hooks/use-tracking'
+import { searchInAny } from 'common/util/parse'
+import { getUser } from 'web/lib/firebase/users'
+import { SiteLink } from 'web/components/site-link'
+import { User } from 'common/user'
+import { SEO } from 'web/components/SEO'
 
 export async function getStaticProps() {
   const txns = await getAllCharityTxns()
@@ -33,6 +37,7 @@ export async function getStaticProps() {
   ])
   const matches = quadraticMatches(txns, totalRaised)
   const numDonors = uniqBy(txns, (txn) => txn.fromId).length
+  const mostRecentDonor = await getUser(txns[txns.length - 1].fromId)
 
   return {
     props: {
@@ -41,6 +46,7 @@ export async function getStaticProps() {
       matches,
       txns,
       numDonors,
+      mostRecentDonor,
     },
     revalidate: 60,
   }
@@ -49,22 +55,28 @@ export async function getStaticProps() {
 type Stat = {
   name: string
   stat: string
+  url?: string
 }
 
 function DonatedStats(props: { stats: Stat[] }) {
   const { stats } = props
   return (
     <dl className="mt-3 grid grid-cols-1 gap-5 rounded-lg bg-gradient-to-r from-pink-300 via-purple-300 to-indigo-400 p-4 sm:grid-cols-3">
-      {stats.map((item) => (
+      {stats.map((stat) => (
         <div
-          key={item.name}
+          key={stat.name}
           className="overflow-hidden rounded-lg bg-white px-4 py-5 shadow sm:p-6"
         >
           <dt className="truncate text-sm font-medium text-gray-500">
-            {item.name}
+            {stat.name}
           </dt>
+
           <dd className="mt-1 text-3xl font-semibold text-gray-900">
-            {item.stat}
+            {stat.url ? (
+              <SiteLink href={stat.url}>{stat.stat}</SiteLink>
+            ) : (
+              <span>{stat.stat}</span>
+            )}
           </dd>
         </div>
       ))}
@@ -78,8 +90,9 @@ export default function Charity(props: {
   matches: { [charityId: string]: number }
   txns: Txn[]
   numDonors: number
+  mostRecentDonor: User
 }) {
-  const { totalRaised, charities, matches, numDonors } = props
+  const { totalRaised, charities, matches, numDonors, mostRecentDonor } = props
 
   const [query, setQuery] = useState('')
   const debouncedQuery = debounce(setQuery, 50)
@@ -88,10 +101,12 @@ export default function Charity(props: {
     () =>
       charities.filter(
         (charity) =>
-          charity.name.toLowerCase().includes(query.toLowerCase()) ||
-          charity.preview.toLowerCase().includes(query.toLowerCase()) ||
-          charity.description.toLowerCase().includes(query.toLowerCase()) ||
-          (charity.tags as string[])?.includes(query.toLowerCase())
+          searchInAny(
+            query,
+            charity.name,
+            charity.preview,
+            charity.description
+          ) || (charity.tags as string[])?.includes(query.toLowerCase())
       ),
     [charities, query]
   )
@@ -100,10 +115,15 @@ export default function Charity(props: {
 
   return (
     <Page>
+      <SEO
+        title="Manifold for Charity"
+        description="Donate your prediction market earnings to charity on Manifold."
+        url="/charity"
+      />
       <Col className="w-full rounded px-4 py-6 sm:px-8 xl:w-[125%]">
         <Col className="">
           <Title className="!mt-0" text="Manifold for Charity" />
-          <span className="text-gray-600">
+          {/* <span className="text-gray-600">
             Through July 15, up to $25k of donations will be matched via{' '}
             <SiteLink href="https://wtfisqf.com/" className="font-bold">
               quadratic funding
@@ -113,6 +133,9 @@ export default function Charity(props: {
               the FTX Future Fund
             </SiteLink>
             !
+          </span> */}
+          <span className="text-gray-600">
+            Convert your M$ earnings into real charitable donations.
           </span>
           <DonatedStats
             stats={[
@@ -125,8 +148,9 @@ export default function Charity(props: {
                 stat: `${numDonors}`,
               },
               {
-                name: 'Matched via quadratic funding',
-                stat: manaToUSD(sum(Object.values(matches))),
+                name: 'Most recent donor',
+                stat: mostRecentDonor.name ?? 'Nobody',
+                url: `/${mostRecentDonor.username}`,
               },
             ]}
           />
diff --git a/web/pages/contract-search-firestore.tsx b/web/pages/contract-search-firestore.tsx
index 3ac11993..2d45e831 100644
--- a/web/pages/contract-search-firestore.tsx
+++ b/web/pages/contract-search-firestore.tsx
@@ -1,4 +1,5 @@
 import { Answer } from 'common/answer'
+import { searchInAny } from 'common/util/parse'
 import { sortBy } from 'lodash'
 import { useState } from 'react'
 import { ContractsGrid } from 'web/components/contract/contracts-list'
@@ -28,22 +29,14 @@ export default function ContractSearchFirestore(props: {
   const [sort, setSort] = useState(initialSort || 'newest')
   const [query, setQuery] = useState(initialQuery)
 
-  const queryWords = query.toLowerCase().split(' ')
-  function check(corpus: string) {
-    return queryWords.every((word) => corpus.toLowerCase().includes(word))
-  }
-
-  let matches = (contracts ?? []).filter(
-    (c) =>
-      check(c.question) ||
-      check(c.creatorName) ||
-      check(c.creatorUsername) ||
-      check(c.lowercaseTags.map((tag) => `#${tag}`).join(' ')) ||
-      check(
-        ((c as any).answers ?? [])
-          .map((answer: Answer) => answer.text)
-          .join(' ')
-      )
+  let matches = (contracts ?? []).filter((c) =>
+    searchInAny(
+      query,
+      c.question,
+      c.creatorName,
+      c.lowercaseTags.map((tag) => `#${tag}`).join(' '),
+      ((c as any).answers ?? []).map((answer: Answer) => answer.text).join(' ')
+    )
   )
 
   if (sort === 'newest') {
@@ -61,10 +54,8 @@ export default function ContractSearchFirestore(props: {
     )
   } else if (sort === 'most-traded') {
     matches.sort((a, b) => b.volume - a.volume)
-  } else if (sort === 'most-popular') {
-    matches.sort(
-      (a, b) => (b.uniqueBettorCount ?? 0) - (a.uniqueBettorCount ?? 0)
-    )
+  } else if (sort === 'score') {
+    matches.sort((a, b) => (b.popularityScore ?? 0) - (a.popularityScore ?? 0))
   } else if (sort === '24-hour-vol') {
     // Use lodash for stable sort, so previous sort breaks all ties.
     matches = sortBy(matches, ({ volume7Days }) => -1 * volume7Days)
@@ -111,7 +102,7 @@ export default function ContractSearchFirestore(props: {
         >
           <option value="newest">Newest</option>
           <option value="oldest">Oldest</option>
-          <option value="most-popular">Most popular</option>
+          <option value="score">Most popular</option>
           <option value="most-traded">Most traded</option>
           <option value="24-hour-vol">24h volume</option>
           <option value="close-date">Closing soon</option>
diff --git a/web/pages/create.tsx b/web/pages/create.tsx
index ebbf2c82..fb87ff4d 100644
--- a/web/pages/create.tsx
+++ b/web/pages/create.tsx
@@ -19,7 +19,7 @@ import {
 import { formatMoney } from 'common/util/format'
 import { removeUndefinedProps } from 'common/util/object'
 import { ChoicesToggleGroup } from 'web/components/choices-toggle-group'
-import { setContractGroupSlugs, getGroup } from 'web/lib/firebase/groups'
+import { getGroup, setContractGroupLinks } from 'web/lib/firebase/groups'
 import { Group } from 'common/group'
 import { useTracking } from 'web/hooks/use-tracking'
 import { useWarnUnsavedChanges } from 'web/hooks/use-warn-unsaved-changes'
@@ -29,6 +29,11 @@ import { User } from 'common/user'
 import { TextEditor, useTextEditor } from 'web/components/editor'
 import { Checkbox } from 'web/components/checkbox'
 import { ENV_CONFIG } from 'common/envs/constants'
+import { redirectIfLoggedOut } from 'web/lib/firebase/server-auth'
+import { Title } from 'web/components/title'
+import { SEO } from 'web/components/SEO'
+
+export const getServerSideProps = redirectIfLoggedOut('/')
 
 type NewQuestionParams = {
   groupId?: string
@@ -70,8 +75,15 @@ export default function Create() {
 
   return (
     <Page>
+      <SEO
+        title="Create a market"
+        description="Create a play-money prediction market on any question."
+        url="/create"
+      />
       <div className="mx-auto w-full max-w-2xl">
         <div className="rounded-lg px-6 py-4 sm:py-0">
+          <Title className="!mt-0" text="Create a market" />
+
           <form>
             <div className="form-control w-full">
               <label className="label">
@@ -100,7 +112,7 @@ export default function Create() {
 
 // Allow user to create a new contract
 export function NewContract(props: {
-  creator: User
+  creator?: User | null
   question: string
   params?: NewQuestionParams
 }) {
@@ -214,7 +226,7 @@ export function NewContract(props: {
           min,
           max,
           initialValue,
-          isLogScale: (min ?? 0) < 0 ? false : isLogScale,
+          isLogScale,
           groupId: selectedGroup?.id,
         })
       )
@@ -225,7 +237,7 @@ export function NewContract(props: {
         isFree: false,
       })
       if (result && selectedGroup) {
-        await setContractGroupSlugs(selectedGroup, result.id)
+        await setContractGroupLinks(selectedGroup, result.id, creator.id)
       }
 
       await router.push(contractPath(result as Contract))
@@ -301,15 +313,13 @@ export function NewContract(props: {
               />
             </Row>
 
-            {!(min !== undefined && min < 0) && (
-              <Checkbox
-                className="my-2 text-sm"
-                label="Log scale"
-                checked={isLogScale}
-                toggle={() => setIsLogScale(!isLogScale)}
-                disabled={isSubmitting}
-              />
-            )}
+            <Checkbox
+              className="my-2 text-sm"
+              label="Log scale"
+              checked={isLogScale}
+              toggle={() => setIsLogScale(!isLogScale)}
+              disabled={isSubmitting}
+            />
 
             {min !== undefined && max !== undefined && min >= max && (
               <div className="mt-2 mb-2 text-sm text-red-500">
@@ -354,7 +364,7 @@ export function NewContract(props: {
           selectedGroup={selectedGroup}
           setSelectedGroup={setSelectedGroup}
           creator={creator}
-          showSelector={showGroupSelector}
+          options={{ showSelector: showGroupSelector, showLabel: true }}
         />
       </div>
 
@@ -386,12 +396,10 @@ export function NewContract(props: {
             type={'date'}
             className="input input-bordered mt-4"
             onClick={(e) => e.stopPropagation()}
-            onChange={(e) =>
-              setCloseDate(dayjs(e.target.value).format('YYYY-MM-DD') || '')
-            }
+            onChange={(e) => setCloseDate(e.target.value)}
             min={Date.now()}
             disabled={isSubmitting}
-            value={dayjs(closeDate).format('YYYY-MM-DD')}
+            value={closeDate}
           />
           <input
             type={'time'}
diff --git a/web/pages/group/[...slugs]/index.tsx b/web/pages/group/[...slugs]/index.tsx
index a364de43..eebf0619 100644
--- a/web/pages/group/[...slugs]/index.tsx
+++ b/web/pages/group/[...slugs]/index.tsx
@@ -1,27 +1,23 @@
-import { take, sortBy, debounce } from 'lodash'
+import { debounce, sortBy, take } from 'lodash'
+import PlusSmIcon from '@heroicons/react/solid/PlusSmIcon'
 
-import { Group } from 'common/group'
+import { Group, GROUP_CHAT_SLUG } from 'common/group'
 import { Page } from 'web/components/page'
 import { listAllBets } from 'web/lib/firebase/bets'
 import { Contract, listContractsByGroupSlug } from 'web/lib/firebase/contracts'
 import {
-  groupPath,
-  getGroupBySlug,
-  updateGroup,
-  joinGroup,
   addContractToGroup,
+  getGroupBySlug,
+  groupPath,
+  joinGroup,
+  updateGroup,
 } from 'web/lib/firebase/groups'
 import { Row } from 'web/components/layout/row'
 import { UserLink } from 'web/components/user-page'
-import {
-  firebaseLogin,
-  getUser,
-  User,
-  writeReferralInfo,
-} from 'web/lib/firebase/users'
+import { firebaseLogin, getUser, User } from 'web/lib/firebase/users'
 import { Col } from 'web/components/layout/col'
 import { useUser } from 'web/hooks/use-user'
-import { listMembers, useGroup } from 'web/hooks/use-group'
+import { listMembers, useGroup, useMembers } from 'web/hooks/use-group'
 import { useRouter } from 'next/router'
 import { scoreCreators, scoreTraders } from 'common/scoring'
 import { Leaderboard } from 'web/components/leaderboard'
@@ -32,22 +28,15 @@ import { SEO } from 'web/components/SEO'
 import { Linkify } from 'web/components/linkify'
 import { fromPropz, usePropz } from 'web/hooks/use-propz'
 import { Tabs } from 'web/components/layout/tabs'
-import {
-  createButtonStyle,
-  CreateQuestionButton,
-} from 'web/components/create-question-button'
-import React, { useEffect, useState } from 'react'
+import { CreateQuestionButton } from 'web/components/create-question-button'
+import React, { useState } from 'react'
 import { GroupChat } from 'web/components/groups/group-chat'
 import { LoadingIndicator } from 'web/components/loading-indicator'
 import { Modal } from 'web/components/layout/modal'
-import {
-  checkAgainstQuery,
-  getSavedSort,
-} from 'web/hooks/use-sort-and-query-params'
+import { getSavedSort } from 'web/hooks/use-sort-and-query-params'
 import { ChoicesToggleGroup } from 'web/components/choices-toggle-group'
 import { toast } from 'react-hot-toast'
 import { useCommentsOnGroup } from 'web/hooks/use-comments'
-import { ShareIconButton } from 'web/components/share-icon-button'
 import { REFERRAL_AMOUNT } from 'common/user'
 import { ContractSearch } from 'web/components/contract-search'
 import clsx from 'clsx'
@@ -55,6 +44,11 @@ import { FollowList } from 'web/components/follow-list'
 import { SearchIcon } from '@heroicons/react/outline'
 import { useTipTxns } from 'web/hooks/use-tip-txns'
 import { JoinOrLeaveGroupButton } from 'web/components/groups/groups-button'
+import { searchInAny } from 'common/util/parse'
+import { useWindowSize } from 'web/hooks/use-window-size'
+import { CopyLinkButton } from 'web/components/copy-link-button'
+import { ENV_CONFIG } from 'common/envs/constants'
+import { useSaveReferral } from 'web/hooks/use-save-referral'
 
 export const getStaticProps = fromPropz(getStaticPropz)
 export async function getStaticPropz(props: { params: { slugs: string[] } }) {
@@ -114,9 +108,9 @@ export async function getStaticPaths() {
 }
 const groupSubpages = [
   undefined,
-  'chat',
-  'questions',
-  'rankings',
+  GROUP_CHAT_SLUG,
+  'markets',
+  'leaderboards',
   'about',
 ] as const
 
@@ -157,13 +151,16 @@ export default function GroupPage(props: {
   const messages = useCommentsOnGroup(group?.id)
 
   const user = useUser()
-  useEffect(() => {
-    const { referrer } = router.query as {
-      referrer?: string
-    }
-    if (!user && router.isReady)
-      writeReferralInfo(creator.username, undefined, referrer, group?.slug)
-  }, [user, creator, group, router])
+
+  useSaveReferral(user, {
+    defaultReferrer: creator.username,
+    groupId: group?.id,
+  })
+
+  const { width } = useWindowSize()
+  const chatDisabled = !group || group.chatDisabled
+  const showChatSidebar = !chatDisabled && (width ?? 1280) >= 1280
+  const showChatTab = !chatDisabled && !showChatSidebar
 
   if (group === null || !groupSubpages.includes(page) || slugs[2]) {
     return <Custom404 />
@@ -172,11 +169,6 @@ export default function GroupPage(props: {
   const isCreator = user && group && user.id === group.creatorId
   const isMember = user && memberIds.includes(user.id)
 
-  const rightSidebar = (
-    <Col className="mt-6 hidden xl:block">
-      <JoinOrCreateButton group={group} user={user} isMember={!!isMember} />
-    </Col>
-  )
   const leaderboard = (
     <Col>
       <GroupLeaderboards
@@ -202,43 +194,46 @@ export default function GroupPage(props: {
     </Col>
   )
 
+  const chatTab = (
+    <Col className="">
+      {messages ? (
+        <GroupChat messages={messages} user={user} group={group} tips={tips} />
+      ) : (
+        <LoadingIndicator />
+      )}
+    </Col>
+  )
+
+  const questionsTab = (
+    <ContractSearch
+      querySortOptions={{
+        shouldLoadFromStorage: true,
+        defaultSort: getSavedSort() ?? 'newest',
+        defaultFilter: 'open',
+      }}
+      additionalFilter={{ groupSlug: group.slug }}
+    />
+  )
+
   const tabs = [
-    ...(group.chatDisabled
+    ...(!showChatTab
       ? []
       : [
           {
             title: 'Chat',
-            content: messages ? (
-              <GroupChat
-                messages={messages}
-                user={user}
-                group={group}
-                tips={tips}
-              />
-            ) : (
-              <LoadingIndicator />
-            ),
-            href: groupPath(group.slug, 'chat'),
+            content: chatTab,
+            href: groupPath(group.slug, GROUP_CHAT_SLUG),
           },
         ]),
     {
-      title: 'Questions',
-      content: (
-        <ContractSearch
-          querySortOptions={{
-            shouldLoadFromStorage: true,
-            defaultSort: getSavedSort() ?? 'newest',
-            defaultFilter: 'open',
-          }}
-          additionalFilter={{ groupSlug: group.slug }}
-        />
-      ),
-      href: groupPath(group.slug, 'questions'),
+      title: 'Markets',
+      content: questionsTab,
+      href: groupPath(group.slug, 'markets'),
     },
     {
-      title: 'Rankings',
+      title: 'Leaderboards',
       content: leaderboard,
-      href: groupPath(group.slug, 'rankings'),
+      href: groupPath(group.slug, 'leaderboards'),
     },
     {
       title: 'About',
@@ -246,22 +241,24 @@ export default function GroupPage(props: {
       href: groupPath(group.slug, 'about'),
     },
   ]
-  const tabIndex = tabs.map((t) => t.title).indexOf(page ?? 'chat')
+  const tabIndex = tabs.map((t) => t.title).indexOf(page ?? GROUP_CHAT_SLUG)
+
   return (
-    <Page rightSidebar={rightSidebar} className="!pb-0">
+    <Page
+      rightSidebar={showChatSidebar ? chatTab : undefined}
+      rightSidebarClassName={showChatSidebar ? '!top-0' : ''}
+      className={showChatSidebar ? '!max-w-7xl !pb-0' : ''}
+    >
       <SEO
         title={group.name}
         description={`Created by ${creator.name}. ${group.about}`}
         url={groupPath(group.slug)}
       />
-
       <Col className="px-3">
         <Row className={'items-center justify-between gap-4'}>
           <div className={'sm:mb-1'}>
             <div
-              className={
-                'line-clamp-1 my-1 text-lg text-indigo-700 sm:my-3 sm:text-2xl'
-              }
+              className={'line-clamp-1 my-2 text-2xl text-indigo-700 sm:my-3'}
             >
               {group.name}
             </div>
@@ -269,19 +266,15 @@ export default function GroupPage(props: {
               <Linkify text={group.about} />
             </div>
           </div>
-          <div className="hidden sm:block xl:hidden">
-            <JoinOrCreateButton
+          <div className="mt-2">
+            <JoinOrAddQuestionsButtons
               group={group}
               user={user}
               isMember={!!isMember}
             />
           </div>
         </Row>
-        <div className="block sm:hidden">
-          <JoinOrCreateButton group={group} user={user} isMember={!!isMember} />
-        </div>
       </Col>
-
       <Tabs
         currentPageForAnalytics={groupPath(group.slug)}
         className={'mb-0 sm:mb-2'}
@@ -292,28 +285,14 @@ export default function GroupPage(props: {
   )
 }
 
-function JoinOrCreateButton(props: {
+function JoinOrAddQuestionsButtons(props: {
   group: Group
   user: User | null | undefined
   isMember: boolean
 }) {
   const { group, user, isMember } = props
   return user && isMember ? (
-    <Row
-      className={'-mt-2 justify-between sm:mt-0 sm:flex-col sm:justify-center'}
-    >
-      <CreateQuestionButton
-        user={user}
-        overrideText={'Add a new question'}
-        className={'hidden w-48 flex-shrink-0 sm:block'}
-        query={`?groupId=${group.id}`}
-      />
-      <CreateQuestionButton
-        user={user}
-        overrideText={'New question'}
-        className={'block w-40 flex-shrink-0 sm:hidden'}
-        query={`?groupId=${group.id}`}
-      />
+    <Row className={'mt-0 justify-end'}>
       <AddContractButton group={group} user={user} />
     </Row>
   ) : group.anyoneCanJoin ? (
@@ -344,6 +323,11 @@ function GroupOverview(props: {
     })
   }
 
+  const postFix = user ? '?referrer=' + user.username : ''
+  const shareUrl = `https://${ENV_CONFIG.domain}${groupPath(
+    group.slug
+  )}${postFix}`
+
   return (
     <>
       <Col className="gap-2 rounded-b bg-white p-2">
@@ -388,22 +372,27 @@ function GroupOverview(props: {
             </span>
           )}
         </Row>
+
         {anyoneCanJoin && user && (
-          <Row className={'flex-wrap items-center gap-1'}>
-            <span className={'text-gray-500'}>Share</span>
-            <ShareIconButton
-              group={group}
-              username={user.username}
-              buttonClassName={'hover:bg-gray-300 mt-1 !text-gray-700'}
-            >
-              <span className={'mx-2'}>
-                Invite a friend and get M${REFERRAL_AMOUNT} if they sign up!
-              </span>
-            </ShareIconButton>
-          </Row>
+          <Col className="my-4 px-2">
+            <div className="text-lg">Invite</div>
+            <div className={'mb-2 text-gray-500'}>
+              Invite a friend to this group and get M${REFERRAL_AMOUNT} if they
+              sign up!
+            </div>
+
+            <CopyLinkButton
+              url={shareUrl}
+              tracking="copy group share link"
+              buttonClassName="btn-md rounded-l-none"
+              toastClassName={'-left-28 mt-1'}
+            />
+          </Col>
         )}
+
         <Col className={'mt-2'}>
-          <GroupMemberSearch members={members} />
+          <div className="mb-2 text-lg">Members</div>
+          <GroupMemberSearch members={members} group={group} />
         </Col>
       </Col>
     </>
@@ -426,14 +415,20 @@ function SearchBar(props: { setQuery: (query: string) => void }) {
   )
 }
 
-function GroupMemberSearch(props: { members: User[] }) {
+function GroupMemberSearch(props: { members: User[]; group: Group }) {
   const [query, setQuery] = useState('')
-  const { members } = props
+  const { group } = props
+  let { members } = props
+
+  // Use static members on load, but also listen to member changes:
+  const listenToMembers = useMembers(group)
+  if (listenToMembers) {
+    members = listenToMembers
+  }
 
   // TODO use find-active-contracts to sort by?
-  const matches = sortBy(members, [(member) => member.name]).filter(
-    (m) =>
-      checkAgainstQuery(query, m.name) || checkAgainstQuery(query, m.username)
+  const matches = sortBy(members, [(member) => member.name]).filter((m) =>
+    searchInAny(query, m.name, m.username)
   )
   const matchLimit = 25
 
@@ -497,14 +492,14 @@ function GroupLeaderboards(props: {
             <SortedLeaderboard
               users={members}
               scoreFunction={(user) => traderScores[user.id] ?? 0}
-              title="🏅 Bettor rankings"
+              title="🏅 Top traders"
               header="Profit"
               maxToShow={maxToShow}
             />
             <SortedLeaderboard
               users={members}
               scoreFunction={(user) => creatorScores[user.id] ?? 0}
-              title="🏅 Creator rankings"
+              title="🏅 Top creators"
               header="Market volume"
               maxToShow={maxToShow}
             />
@@ -513,7 +508,7 @@ function GroupLeaderboards(props: {
           <>
             <Leaderboard
               className="max-w-xl"
-              title="🏅 Top bettors"
+              title="🏅 Top traders"
               users={topTraders}
               columns={[
                 {
@@ -544,26 +539,49 @@ function GroupLeaderboards(props: {
 }
 
 function AddContractButton(props: { group: Group; user: User }) {
-  const { group } = props
+  const { group, user } = props
   const [open, setOpen] = useState(false)
 
   async function addContractToCurrentGroup(contract: Contract) {
-    await addContractToGroup(group, contract)
+    await addContractToGroup(group, contract, user.id)
     setOpen(false)
   }
 
   return (
     <>
+      <div className={'flex justify-center'}>
+        <button
+          className={clsx('btn btn-sm btn-outline')}
+          onClick={() => setOpen(true)}
+        >
+          <PlusSmIcon className="h-6 w-6" aria-hidden="true" /> question
+        </button>
+      </div>
+
       <Modal open={open} setOpen={setOpen} className={'sm:p-0'}>
         <Col
           className={
-            'max-h-[60vh] min-h-[60vh] w-full gap-4 rounded-md bg-white p-8'
+            'max-h-[60vh] min-h-[60vh] w-full gap-4 rounded-md bg-white'
           }
         >
-          <div className={'text-lg text-indigo-700'}>
-            Add a question to your group
-          </div>
-          <div className={'overflow-y-scroll p-1'}>
+          <Col className="p-8 pb-0">
+            <div className={'text-xl text-indigo-700'}>
+              Add a question to your group
+            </div>
+
+            <Col className="items-center">
+              <CreateQuestionButton
+                user={user}
+                overrideText={'New question'}
+                className={'w-48 flex-shrink-0 '}
+                query={`?groupId=${group.id}`}
+              />
+
+              <div className={'mt-2 text-lg text-indigo-700'}>or</div>
+            </Col>
+          </Col>
+
+          <div className={'overflow-y-scroll sm:px-8'}>
             <ContractSearch
               hideOrderSelector={true}
               onContractClick={addContractToCurrentGroup}
@@ -575,26 +593,6 @@ function AddContractButton(props: { group: Group; user: User }) {
           </div>
         </Col>
       </Modal>
-      <div className={'flex justify-center'}>
-        <button
-          className={clsx(
-            createButtonStyle,
-            'hidden w-48 whitespace-nowrap border border-black text-black hover:bg-black hover:text-white sm:block'
-          )}
-          onClick={() => setOpen(true)}
-        >
-          Add an old question
-        </button>
-        <button
-          className={clsx(
-            createButtonStyle,
-            'block w-40 whitespace-nowrap border border-black text-black hover:bg-black hover:text-white sm:hidden'
-          )}
-          onClick={() => setOpen(true)}
-        >
-          Old question
-        </button>
-      </div>
     </>
   )
 }
diff --git a/web/pages/groups.tsx b/web/pages/groups.tsx
index 2523b789..521742b2 100644
--- a/web/pages/groups.tsx
+++ b/web/pages/groups.tsx
@@ -1,4 +1,4 @@
-import { sortBy, debounce } from 'lodash'
+import { debounce, sortBy } from 'lodash'
 import Link from 'next/link'
 import React, { useEffect, useState } from 'react'
 import { Group } from 'common/group'
@@ -12,12 +12,13 @@ import { useUser } from 'web/hooks/use-user'
 import { groupPath, listAllGroups } from 'web/lib/firebase/groups'
 import { getUser, User } from 'web/lib/firebase/users'
 import { Tabs } from 'web/components/layout/tabs'
-import { checkAgainstQuery } from 'web/hooks/use-sort-and-query-params'
 import { SiteLink } from 'web/components/site-link'
 import clsx from 'clsx'
 import { Avatar } from 'web/components/avatar'
 import { JoinOrLeaveGroupButton } from 'web/components/groups/groups-button'
 import { UserLink } from 'web/components/user-page'
+import { searchInAny } from 'common/util/parse'
+import { SEO } from 'web/components/SEO'
 
 export async function getStaticProps() {
   const groups = await listAllGroups().catch((_) => [])
@@ -71,20 +72,28 @@ export default function Groups(props: {
   const matches = sortBy(groups, [
     (group) => -1 * group.contractIds.length,
     (group) => -1 * group.memberIds.length,
-  ]).filter(
-    (g) =>
-      checkAgainstQuery(query, g.name) ||
-      checkAgainstQuery(query, g.about || '') ||
-      checkAgainstQuery(query, creatorsDict[g.creatorId].username)
+  ]).filter((g) =>
+    searchInAny(
+      query,
+      g.name,
+      g.about || '',
+      creatorsDict[g.creatorId].username
+    )
   )
 
   const matchesOrderedByRecentActivity = sortBy(groups, [
-    (group) => -1 * group.mostRecentActivityTime,
-  ]).filter(
-    (g) =>
-      checkAgainstQuery(query, g.name) ||
-      checkAgainstQuery(query, g.about || '') ||
-      checkAgainstQuery(query, creatorsDict[g.creatorId].username)
+    (group) =>
+      -1 *
+      (group.mostRecentChatActivityTime ??
+        group.mostRecentContractAddedTime ??
+        group.mostRecentActivityTime),
+  ]).filter((g) =>
+    searchInAny(
+      query,
+      g.name,
+      g.about || '',
+      creatorsDict[g.creatorId].username
+    )
   )
 
   // Not strictly necessary, but makes the "hold delete" experience less laggy
@@ -92,6 +101,11 @@ export default function Groups(props: {
 
   return (
     <Page>
+      <SEO
+        title="Groups"
+        description="Manifold Groups are communities centered around a collection of prediction markets. Discuss and compete on questions with your friends."
+        url="/groups"
+      />
       <Col className="items-center">
         <Col className="w-full max-w-2xl px-4 sm:px-2">
           <Row className="items-center justify-between">
@@ -177,7 +191,7 @@ export function GroupCard(props: { group: Group; creator: User | undefined }) {
       </Link>
       <div>
         <Avatar
-          className={'absolute top-2 right-2'}
+          className={'absolute top-2 right-2 z-10'}
           username={creator?.username}
           avatarUrl={creator?.avatarUrl}
           noLink={false}
@@ -224,7 +238,7 @@ function GroupMembersList(props: { group: Group }) {
   )
 }
 
-export function GroupLink(props: { group: Group; className?: string }) {
+export function GroupLinkItem(props: { group: Group; className?: string }) {
   const { group, className } = props
 
   return (
diff --git a/web/pages/home.tsx b/web/pages/home.tsx
index 53a873a1..30b93762 100644
--- a/web/pages/home.tsx
+++ b/web/pages/home.tsx
@@ -1,29 +1,28 @@
 import React, { useEffect, useState } from 'react'
-import Router, { useRouter } from 'next/router'
+import { useRouter } from 'next/router'
 import { PlusSmIcon } from '@heroicons/react/solid'
 
 import { Page } from 'web/components/page'
 import { Col } from 'web/components/layout/col'
-import { useUser } from 'web/hooks/use-user'
 import { getSavedSort } from 'web/hooks/use-sort-and-query-params'
-import { ContractSearch } from 'web/components/contract-search'
+import { ContractSearch, DEFAULT_SORT } from 'web/components/contract-search'
 import { Contract } from 'common/contract'
 import { ContractPageContent } from './[username]/[contractSlug]'
 import { getContractFromSlug } from 'web/lib/firebase/contracts'
 import { useTracking } from 'web/hooks/use-tracking'
 import { track } from 'web/lib/service/analytics'
+import { redirectIfLoggedOut } from 'web/lib/firebase/server-auth'
+import { useSaveReferral } from 'web/hooks/use-save-referral'
+
+export const getServerSideProps = redirectIfLoggedOut('/')
 
 const Home = () => {
-  const user = useUser()
   const [contract, setContract] = useContractPage()
 
   const router = useRouter()
   useTracking('view home')
 
-  if (user === null) {
-    Router.replace('/')
-    return <></>
-  }
+  useSaveReferral()
 
   return (
     <>
@@ -32,7 +31,7 @@ const Home = () => {
           <ContractSearch
             querySortOptions={{
               shouldLoadFromStorage: true,
-              defaultSort: getSavedSort() ?? 'most-popular',
+              defaultSort: getSavedSort() ?? DEFAULT_SORT,
             }}
             onContractClick={(c) => {
               // Show contract without navigating to contract page.
diff --git a/web/pages/index.tsx b/web/pages/index.tsx
index 904fc014..473189aa 100644
--- a/web/pages/index.tsx
+++ b/web/pages/index.tsx
@@ -1,14 +1,13 @@
-import React from 'react'
-import Router from 'next/router'
-
 import { Contract, getContractsBySlugs } from 'web/lib/firebase/contracts'
 import { Page } from 'web/components/page'
 import { LandingPagePanel } from 'web/components/landing-page-panel'
 import { Col } from 'web/components/layout/col'
-import { useUser } from 'web/hooks/use-user'
 import { ManifoldLogo } from 'web/components/nav/manifold-logo'
+import { redirectIfLoggedIn } from 'web/lib/firebase/server-auth'
+import { useSaveReferral } from 'web/hooks/use-save-referral'
+import { SEO } from 'web/components/SEO'
 
-export async function getStaticProps() {
+export const getServerSideProps = redirectIfLoggedIn('/home', async (_) => {
   // These hardcoded markets will be shown in the frontpage for signed-out users:
   const hotContracts = await getContractsBySlugs([
     'will-max-go-to-prom-with-a-girl',
@@ -22,25 +21,21 @@ export async function getStaticProps() {
     'will-congress-hold-any-hearings-abo-e21f987033b3',
     'will-at-least-10-world-cities-have',
   ])
+  return { props: { hotContracts } }
+})
 
-  return {
-    props: { hotContracts },
-    revalidate: 60, // regenerate after a minute
-  }
-}
-
-const Home = (props: { hotContracts: Contract[] }) => {
+export default function Home(props: { hotContracts: Contract[] }) {
   const { hotContracts } = props
 
-  const user = useUser()
-
-  if (user) {
-    Router.replace('/home')
-    return <></>
-  }
+  useSaveReferral()
 
   return (
     <Page>
+      <SEO
+        title="Manifold Markets"
+        description="Create a play-money prediction market on any topic you care about
+            and bet with your friends on what will happen!"
+      />
       <div className="px-4 pt-2 md:mt-0 lg:hidden">
         <ManifoldLogo />
       </div>
@@ -58,5 +53,3 @@ const Home = (props: { hotContracts: Contract[] }) => {
     </Page>
   )
 }
-
-export default Home
diff --git a/web/pages/leaderboards.tsx b/web/pages/leaderboards.tsx
index ac7a348f..2188ef73 100644
--- a/web/pages/leaderboards.tsx
+++ b/web/pages/leaderboards.tsx
@@ -9,82 +9,95 @@ import {
   User,
 } from 'web/lib/firebase/users'
 import { formatMoney } from 'common/util/format'
-import { fromPropz, usePropz } from 'web/hooks/use-propz'
 import { useEffect, useState } from 'react'
-import { LoadingIndicator } from 'web/components/loading-indicator'
 import { Title } from 'web/components/title'
 import { Tabs } from 'web/components/layout/tabs'
 import { useTracking } from 'web/hooks/use-tracking'
+import { SEO } from 'web/components/SEO'
+
+export async function getStaticProps() {
+  const props = await fetchProps()
 
-export const getStaticProps = fromPropz(getStaticPropz)
-export async function getStaticPropz() {
-  return queryLeaderboardUsers('allTime')
-}
-const queryLeaderboardUsers = async (period: Period) => {
-  const [topTraders, topCreators, topFollowed] = await Promise.all([
-    getTopTraders(period).catch(() => {}),
-    getTopCreators(period).catch(() => {}),
-    getTopFollowed().catch(() => {}),
-  ])
   return {
-    props: {
-      topTraders,
-      topCreators,
-      topFollowed,
-    },
+    props,
     revalidate: 60, // regenerate after a minute
   }
 }
 
-export default function Leaderboards(props: {
+const fetchProps = async () => {
+  const [allTime, monthly, weekly, daily] = await Promise.all([
+    queryLeaderboardUsers('allTime'),
+    queryLeaderboardUsers('monthly'),
+    queryLeaderboardUsers('weekly'),
+    queryLeaderboardUsers('daily'),
+  ])
+  const topFollowed = await getTopFollowed()
+
+  return {
+    allTime,
+    monthly,
+    weekly,
+    daily,
+    topFollowed,
+  }
+}
+
+const queryLeaderboardUsers = async (period: Period) => {
+  const [topTraders, topCreators] = await Promise.all([
+    getTopTraders(period),
+    getTopCreators(period),
+  ])
+  return {
+    topTraders,
+    topCreators,
+  }
+}
+
+type leaderboard = {
   topTraders: User[]
   topCreators: User[]
+}
+
+export default function Leaderboards(_props: {
+  allTime: leaderboard
+  monthly: leaderboard
+  weekly: leaderboard
+  daily: leaderboard
   topFollowed: User[]
 }) {
-  props = usePropz(props, getStaticPropz) ?? {
-    topTraders: [],
-    topCreators: [],
-    topFollowed: [],
-  }
-  const [topTradersState, setTopTraders] = useState(props.topTraders)
-  const [isLoading, setLoading] = useState(false)
-  const [period, setPeriod] = useState<Period>('allTime')
-
+  const [props, setProps] = useState<Parameters<typeof Leaderboards>[0]>(_props)
   useEffect(() => {
-    setLoading(true)
-    queryLeaderboardUsers(period).then((res) => {
-      setTopTraders(res.props.topTraders as User[])
-      setLoading(false)
-    })
-  }, [period])
+    fetchProps().then((props) => setProps(props))
+  }, [])
 
   const LeaderboardWithPeriod = (period: Period) => {
+    const { topTraders, topCreators } = props[period]
+
     return (
       <>
         <Col className="mx-4 items-center gap-10 lg:flex-row">
-          {!isLoading ? (
-            <>
-              {period === 'allTime' ||
-              period == 'weekly' ||
-              period === 'daily' ? ( //TODO: show other periods once they're available
-                <Leaderboard
-                  title="🏅 Top bettors"
-                  users={topTradersState}
-                  columns={[
-                    {
-                      header: 'Total profit',
-                      renderCell: (user) =>
-                        formatMoney(user.profitCached[period]),
-                    },
-                  ]}
-                />
-              ) : (
-                <></>
-              )}
-            </>
-          ) : (
-            <LoadingIndicator spinnerClassName={'border-gray-500'} />
-          )}
+          <Leaderboard
+            title="🏅 Top traders"
+            users={topTraders}
+            columns={[
+              {
+                header: 'Total profit',
+                renderCell: (user) => formatMoney(user.profitCached[period]),
+              },
+            ]}
+          />
+
+          <Leaderboard
+            title="🏅 Top creators"
+            users={topCreators}
+            columns={[
+              {
+                header: 'Total bet',
+                renderCell: (user) =>
+                  formatMoney(user.creatorVolumeCached[period]),
+              },
+            ]}
+          />
         </Col>
       </>
     )
@@ -93,23 +106,25 @@ export default function Leaderboards(props: {
 
   return (
     <Page>
+      <SEO
+        title="Leaderboards"
+        description="Manifold's leaderboards show the top traders and market creators."
+        url="/leaderboards"
+      />
       <Title text={'Leaderboards'} className={'hidden md:block'} />
       <Tabs
         currentPageForAnalytics={'leaderboards'}
-        defaultIndex={0}
-        onClick={(title, index) => {
-          const period = ['allTime', 'monthly', 'weekly', 'daily'][index]
-          setPeriod(period as Period)
-        }}
+        defaultIndex={1}
         tabs={[
           {
             title: 'All Time',
             content: LeaderboardWithPeriod('allTime'),
           },
-          {
-            title: 'Monthly',
-            content: LeaderboardWithPeriod('monthly'),
-          },
+          // TODO: Enable this near the end of July!
+          // {
+          //   title: 'Monthly',
+          //   content: LeaderboardWithPeriod('monthly'),
+          // },
           {
             title: 'Weekly',
             content: LeaderboardWithPeriod('weekly'),
diff --git a/web/pages/link/[slug].tsx b/web/pages/link/[slug].tsx
index 67e7b695..119fec77 100644
--- a/web/pages/link/[slug].tsx
+++ b/web/pages/link/[slug].tsx
@@ -6,8 +6,9 @@ import { claimManalink } from 'web/lib/firebase/api'
 import { useManalink } from 'web/lib/firebase/manalinks'
 import { ManalinkCard } from 'web/components/manalink-card'
 import { useUser } from 'web/hooks/use-user'
-import { useUserById } from 'web/hooks/use-user'
 import { firebaseLogin } from 'web/lib/firebase/users'
+import { Row } from 'web/components/layout/row'
+import { Button } from 'web/components/button'
 
 export default function ClaimPage() {
   const user = useUser()
@@ -17,7 +18,6 @@ export default function ClaimPage() {
   const [claiming, setClaiming] = useState(false)
   const [error, setError] = useState<string | undefined>(undefined)
 
-  const fromUser = useUserById(manalink?.fromId)
   if (!manalink) {
     return <></>
   }
@@ -30,29 +30,42 @@ export default function ClaimPage() {
         description="Send mana to anyone via link!"
         url="/send"
       />
-      <div className="mx-auto max-w-xl">
-        <Title text={`Claim M$${manalink.amount} mana`} />
-        <ManalinkCard
-          defaultMessage={fromUser?.name || 'Enjoy this mana!'}
-          info={info}
-          isClaiming={claiming}
-          onClaim={async () => {
-            setClaiming(true)
-            try {
-              if (user == null) {
-                await firebaseLogin()
-              }
-              await claimManalink({ slug: manalink.slug })
-              user && router.push(`/${user.username}?claimed-mana=yes`)
-            } catch (e) {
-              console.log(e)
-              const message =
-                e && e instanceof Object ? e.toString() : 'An error occurred.'
-              setError(message)
-            }
-            setClaiming(false)
-          }}
-        />
+      <div className="mx-auto max-w-xl px-2">
+        <Row className="items-center justify-between">
+          <Title text={`Claim M$${manalink.amount} mana`} />
+          <div className="my-auto">
+            <Button
+              onClick={async () => {
+                setClaiming(true)
+                try {
+                  if (user == null) {
+                    await firebaseLogin()
+                    setClaiming(false)
+                    return
+                  }
+                  if (user?.id == manalink.fromId) {
+                    throw new Error("You can't claim your own manalink.")
+                  }
+                  await claimManalink({ slug: manalink.slug })
+                  user && router.push(`/${user.username}?claimed-mana=yes`)
+                } catch (e) {
+                  console.log(e)
+                  const message =
+                    e && e instanceof Object
+                      ? e.toString()
+                      : 'An error occurred.'
+                  setError(message)
+                }
+                setClaiming(false)
+              }}
+              disabled={claiming}
+              size="lg"
+            >
+              {user ? 'Claim' : 'Login'}
+            </Button>
+          </div>
+        </Row>
+        <ManalinkCard info={info} />
         {error && (
           <section className="my-5 text-red-500">
             <p>Failed to claim manalink.</p>
diff --git a/web/pages/links.tsx b/web/pages/links.tsx
index 76c62978..0f91d70c 100644
--- a/web/pages/links.tsx
+++ b/web/pages/links.tsx
@@ -1,7 +1,4 @@
-import clsx from 'clsx'
 import { useState } from 'react'
-import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/solid'
-import { Claim, Manalink } from 'common/manalink'
 import { formatMoney } from 'common/util/format'
 import { Col } from 'web/components/layout/col'
 import { Row } from 'web/components/layout/row'
@@ -11,18 +8,24 @@ import { Title } from 'web/components/title'
 import { Subtitle } from 'web/components/subtitle'
 import { useUser } from 'web/hooks/use-user'
 import { useUserManalinks } from 'web/lib/firebase/manalinks'
-import { fromNow } from 'web/lib/util/time'
 import { useUserById } from 'web/hooks/use-user'
 import { ManalinkTxn } from 'common/txn'
 import { Avatar } from 'web/components/avatar'
 import { RelativeTimestamp } from 'web/components/relative-timestamp'
 import { UserLink } from 'web/components/user-page'
 import { CreateLinksButton } from 'web/components/manalinks/create-links-button'
+import { redirectIfLoggedOut } from 'web/lib/firebase/server-auth'
 
 import dayjs from 'dayjs'
 import customParseFormat from 'dayjs/plugin/customParseFormat'
+import { ManalinkCardFromView } from 'web/components/manalink-card'
+import { Pagination } from 'web/components/pagination'
+import { Manalink } from 'common/manalink'
 dayjs.extend(customParseFormat)
 
+const LINKS_PER_PAGE = 24
+export const getServerSideProps = redirectIfLoggedOut('/')
+
 export function getManalinkUrl(slug: string) {
   return `${location.protocol}//${location.host}/link/${slug}`
 }
@@ -65,12 +68,58 @@ export default function LinkPage() {
           don&apos;t yet have a Manifold account.
         </p>
         <Subtitle text="Your Manalinks" />
-        <LinksTable links={unclaimedLinks} highlightedSlug={highlightedSlug} />
+        <ManalinksDisplay
+          unclaimedLinks={unclaimedLinks}
+          highlightedSlug={highlightedSlug}
+        />
       </Col>
     </Page>
   )
 }
 
+function ManalinksDisplay(props: {
+  unclaimedLinks: Manalink[]
+  highlightedSlug: string
+}) {
+  const { unclaimedLinks, highlightedSlug } = props
+  const [page, setPage] = useState(0)
+  const start = page * LINKS_PER_PAGE
+  const end = start + LINKS_PER_PAGE
+  const displayedLinks = unclaimedLinks.slice(start, end)
+
+  if (unclaimedLinks.length === 0) {
+    return (
+      <p className="text-gray-500">
+        You don't have any unclaimed manalinks. Send some more to spread the
+        wealth!
+      </p>
+    )
+  } else {
+    return (
+      <>
+        <Col className="grid w-full gap-4 md:grid-cols-2">
+          {displayedLinks.map((link) => (
+            <ManalinkCardFromView
+              key={link.slug + link.createdTime}
+              link={link}
+              highlightedSlug={highlightedSlug}
+            />
+          ))}
+        </Col>
+        <Pagination
+          page={page}
+          itemsPerPage={LINKS_PER_PAGE}
+          totalItems={unclaimedLinks.length}
+          setPage={setPage}
+          className="mt-4 bg-transparent"
+          scrollToTop
+        />
+      </>
+    )
+  }
+}
+
+// TODO: either utilize this or get rid of it
 export function ClaimsList(props: { txns: ManalinkTxn[] }) {
   const { txns } = props
   return (
@@ -118,127 +167,3 @@ export function ClaimDescription(props: { txn: ManalinkTxn }) {
     </div>
   )
 }
-
-function ClaimTableRow(props: { claim: Claim }) {
-  const { claim } = props
-  const who = useUserById(claim.toId)
-  return (
-    <tr>
-      <td className="px-5 py-2">{who?.name || 'Loading...'}</td>
-      <td className="px-5 py-2">{`${new Date(
-        claim.claimedTime
-      ).toLocaleString()}, ${fromNow(claim.claimedTime)}`}</td>
-    </tr>
-  )
-}
-
-function LinkDetailsTable(props: { link: Manalink }) {
-  const { link } = props
-  return (
-    <table className="w-full divide-y divide-gray-300 border border-gray-400">
-      <thead className="bg-gray-50 text-left text-sm font-semibold text-gray-900">
-        <tr>
-          <th className="px-5 py-2">Claimed by</th>
-          <th className="px-5 py-2">Time</th>
-        </tr>
-      </thead>
-      <tbody className="divide-y divide-gray-200 bg-white text-sm text-gray-500">
-        {link.claims.length ? (
-          link.claims.map((claim) => <ClaimTableRow claim={claim} />)
-        ) : (
-          <tr>
-            <td className="px-5 py-2" colSpan={2}>
-              No claims yet.
-            </td>
-          </tr>
-        )}
-      </tbody>
-    </table>
-  )
-}
-
-function LinkTableRow(props: { link: Manalink; highlight: boolean }) {
-  const { link, highlight } = props
-  const [expanded, setExpanded] = useState(false)
-  return (
-    <>
-      <LinkSummaryRow
-        link={link}
-        highlight={highlight}
-        expanded={expanded}
-        onToggle={() => setExpanded((exp) => !exp)}
-      />
-      {expanded && (
-        <tr>
-          <td className="bg-gray-100 p-3" colSpan={5}>
-            <LinkDetailsTable link={link} />
-          </td>
-        </tr>
-      )}
-    </>
-  )
-}
-
-function LinkSummaryRow(props: {
-  link: Manalink
-  highlight: boolean
-  expanded: boolean
-  onToggle: () => void
-}) {
-  const { link, highlight, expanded, onToggle } = props
-  const className = clsx(
-    'whitespace-nowrap text-sm hover:cursor-pointer text-gray-500 hover:bg-sky-50 bg-white',
-    highlight ? 'bg-indigo-100 rounded-lg animate-pulse' : ''
-  )
-  return (
-    <tr id={link.slug} key={link.slug} className={className}>
-      <td className="py-4 pl-5" onClick={onToggle}>
-        {expanded ? (
-          <ChevronUpIcon className="h-5 w-5" />
-        ) : (
-          <ChevronDownIcon className="h-5 w-5" />
-        )}
-      </td>
-
-      <td className="px-5 py-4 font-medium text-gray-900">
-        {formatMoney(link.amount)}
-      </td>
-      <td className="px-5 py-4">{getManalinkUrl(link.slug)}</td>
-      <td className="px-5 py-4">{link.claimedUserIds.length}</td>
-      <td className="px-5 py-4">{link.maxUses == null ? '∞' : link.maxUses}</td>
-      <td className="px-5 py-4">
-        {link.expiresTime == null ? 'Never' : fromNow(link.expiresTime)}
-      </td>
-    </tr>
-  )
-}
-
-function LinksTable(props: { links: Manalink[]; highlightedSlug?: string }) {
-  const { links, highlightedSlug } = props
-  return links.length == 0 ? (
-    <p>You don&apos;t currently have any outstanding manalinks.</p>
-  ) : (
-    <div className="overflow-scroll">
-      <table className="w-full divide-y divide-gray-300 rounded-lg border border-gray-200">
-        <thead className="bg-gray-50 text-left text-sm font-semibold text-gray-900">
-          <tr>
-            <th></th>
-            <th className="px-5 py-3.5">Amount</th>
-            <th className="px-5 py-3.5">Link</th>
-            <th className="px-5 py-3.5">Uses</th>
-            <th className="px-5 py-3.5">Max Uses</th>
-            <th className="px-5 py-3.5">Expires</th>
-          </tr>
-        </thead>
-        <tbody className="divide-y divide-gray-200 bg-white">
-          {links.map((link) => (
-            <LinkTableRow
-              link={link}
-              highlight={link.slug === highlightedSlug}
-            />
-          ))}
-        </tbody>
-      </table>
-    </div>
-  )
-}
diff --git a/web/pages/markets.tsx b/web/pages/markets.tsx
index a3e851fc..2d3346c1 100644
--- a/web/pages/markets.tsx
+++ b/web/pages/markets.tsx
@@ -8,7 +8,7 @@ export default function Markets() {
     <Page>
       <SEO
         title="Explore"
-        description="Discover what's new, trending, or soon-to-close. Or search among our hundreds of markets."
+        description="Discover what's new, trending, or soon-to-close. Or search thousands of prediction markets."
         url="/markets"
       />
       <ContractSearch />
diff --git a/web/pages/notifications.tsx b/web/pages/notifications.tsx
index f86c4fef..72754d32 100644
--- a/web/pages/notifications.tsx
+++ b/web/pages/notifications.tsx
@@ -1,6 +1,6 @@
 import { Tabs } from 'web/components/layout/tabs'
-import { usePrivateUser, useUser } from 'web/hooks/use-user'
-import React, { useEffect, useState } from 'react'
+import { usePrivateUser } from 'web/hooks/use-user'
+import React, { useEffect, useMemo, useState } from 'react'
 import { Notification, notification_source_types } from 'common/notification'
 import { Avatar, EmptyAvatar } from 'web/components/avatar'
 import { Row } from 'web/components/layout/row'
@@ -9,10 +9,13 @@ import { Title } from 'web/components/title'
 import { doc, updateDoc } from 'firebase/firestore'
 import { db } from 'web/lib/firebase/init'
 import { UserLink } from 'web/components/user-page'
-import { notification_subscribe_types, PrivateUser } from 'common/user'
-import { ChoicesToggleGroup } from 'web/components/choices-toggle-group'
-import { listenForPrivateUser, updatePrivateUser } from 'web/lib/firebase/users'
-import { LoadingIndicator } from 'web/components/loading-indicator'
+import {
+  MANIFOLD_AVATAR_URL,
+  MANIFOLD_USERNAME,
+  PrivateUser,
+  User,
+} from 'common/user'
+import { getUser } from 'web/lib/firebase/users'
 import clsx from 'clsx'
 import { RelativeTimestamp } from 'web/components/relative-timestamp'
 import { Linkify } from 'web/components/linkify'
@@ -27,8 +30,7 @@ import {
   NotificationGroup,
   usePreferredGroupedNotifications,
 } from 'web/hooks/use-notifications'
-import { CheckIcon, TrendingUpIcon, XIcon } from '@heroicons/react/outline'
-import toast from 'react-hot-toast'
+import { TrendingUpIcon } from '@heroicons/react/outline'
 import { formatMoney } from 'common/util/format'
 import { groupPath } from 'web/lib/firebase/groups'
 import { UNIQUE_BETTOR_BONUS_AMOUNT } from 'common/numeric-constants'
@@ -37,13 +39,40 @@ import Custom404 from 'web/pages/404'
 import { track } from '@amplitude/analytics-browser'
 import { Pagination } from 'web/components/pagination'
 import { useWindowSize } from 'web/hooks/use-window-size'
+import { safeLocalStorage } from 'web/lib/util/local'
+import {
+  getServerAuthenticatedUid,
+  redirectIfLoggedOut,
+} from 'web/lib/firebase/server-auth'
+import { SiteLink } from 'web/components/site-link'
+import { NotificationSettings } from 'web/components/NotificationSettings'
 
 export const NOTIFICATIONS_PER_PAGE = 30
 const MULTIPLE_USERS_KEY = 'multipleUsers'
+const HIGHLIGHT_CLASS = 'bg-indigo-50'
 
-export default function Notifications() {
-  const user = useUser()
+export const getServerSideProps = redirectIfLoggedOut('/', async (ctx) => {
+  const uid = await getServerAuthenticatedUid(ctx)
+  if (!uid) {
+    return { props: { user: null } }
+  }
+  const user = await getUser(uid)
+  return { props: { user } }
+})
+
+export default function Notifications(props: { user: User }) {
+  const { user } = props
   const privateUser = usePrivateUser(user?.id)
+  const local = safeLocalStorage()
+  let localNotifications = [] as Notification[]
+  const localSavedNotificationGroups = local?.getItem('notification-groups')
+  let localNotificationGroups = [] as NotificationGroup[]
+  if (localSavedNotificationGroups) {
+    localNotificationGroups = JSON.parse(localSavedNotificationGroups)
+    localNotifications = localNotificationGroups
+      .map((g) => g.notifications)
+      .flat()
+  }
 
   if (!user) return <Custom404 />
   return (
@@ -60,9 +89,16 @@ export default function Notifications() {
               {
                 title: 'Notifications',
                 content: privateUser ? (
-                  <NotificationsList privateUser={privateUser} />
+                  <NotificationsList
+                    privateUser={privateUser}
+                    cachedNotifications={localNotifications}
+                  />
                 ) : (
-                  <LoadingIndicator />
+                  <div className={'min-h-[100vh]'}>
+                    <RenderNotificationGroups
+                      notificationGroups={localNotificationGroups}
+                    />
+                  </div>
                 ),
               },
               {
@@ -81,39 +117,13 @@ export default function Notifications() {
   )
 }
 
-function NotificationsList(props: { privateUser: PrivateUser }) {
-  const { privateUser } = props
-  const [page, setPage] = useState(0)
-  const allGroupedNotifications = usePreferredGroupedNotifications(privateUser)
-  const [paginatedGroupedNotifications, setPaginatedGroupedNotifications] =
-    useState<NotificationGroup[] | undefined>(undefined)
-
-  useEffect(() => {
-    if (!allGroupedNotifications) return
-    const start = page * NOTIFICATIONS_PER_PAGE
-    const end = start + NOTIFICATIONS_PER_PAGE
-    const maxNotificationsToShow = allGroupedNotifications.slice(start, end)
-    const remainingNotification = allGroupedNotifications.slice(end)
-    for (const notification of remainingNotification) {
-      if (notification.isSeen) break
-      else setNotificationsAsSeen(notification.notifications)
-    }
-    setPaginatedGroupedNotifications(maxNotificationsToShow)
-  }, [allGroupedNotifications, page])
-
-  if (!paginatedGroupedNotifications || !allGroupedNotifications)
-    return <LoadingIndicator />
-
+function RenderNotificationGroups(props: {
+  notificationGroups: NotificationGroup[]
+}) {
+  const { notificationGroups } = props
   return (
-    <div className={'min-h-[100vh]'}>
-      {paginatedGroupedNotifications.length === 0 && (
-        <div className={'mt-2'}>
-          You don't have any notifications. Try changing your settings to see
-          more.
-        </div>
-      )}
-
-      {paginatedGroupedNotifications.map((notification) =>
+    <>
+      {notificationGroups.map((notification) =>
         notification.type === 'income' ? (
           <IncomeNotificationGroupItem
             notificationGroup={notification}
@@ -131,6 +141,52 @@ function NotificationsList(props: { privateUser: PrivateUser }) {
           />
         )
       )}
+    </>
+  )
+}
+
+function NotificationsList(props: {
+  privateUser: PrivateUser
+  cachedNotifications: Notification[]
+}) {
+  const { privateUser, cachedNotifications } = props
+  const [page, setPage] = useState(0)
+  const allGroupedNotifications = usePreferredGroupedNotifications(
+    privateUser,
+    cachedNotifications
+  )
+  const paginatedGroupedNotifications = useMemo(() => {
+    if (!allGroupedNotifications) return
+    const start = page * NOTIFICATIONS_PER_PAGE
+    const end = start + NOTIFICATIONS_PER_PAGE
+    const maxNotificationsToShow = allGroupedNotifications.slice(start, end)
+    const remainingNotification = allGroupedNotifications.slice(end)
+    for (const notification of remainingNotification) {
+      if (notification.isSeen) break
+      else setNotificationsAsSeen(notification.notifications)
+    }
+    const local = safeLocalStorage()
+    local?.setItem(
+      'notification-groups',
+      JSON.stringify(allGroupedNotifications)
+    )
+    return maxNotificationsToShow
+  }, [allGroupedNotifications, page])
+
+  if (!paginatedGroupedNotifications || !allGroupedNotifications) return <div />
+
+  return (
+    <div className={'min-h-[100vh]'}>
+      {paginatedGroupedNotifications.length === 0 && (
+        <div className={'mt-2'}>
+          You don't have any notifications. Try changing your settings to see
+          more.
+        </div>
+      )}
+
+      <RenderNotificationGroups
+        notificationGroups={paginatedGroupedNotifications}
+      />
       {paginatedGroupedNotifications.length > 0 &&
         allGroupedNotifications.length > NOTIFICATIONS_PER_PAGE && (
           <Pagination
@@ -139,6 +195,8 @@ function NotificationsList(props: { privateUser: PrivateUser }) {
             totalItems={allGroupedNotifications.length}
             setPage={setPage}
             scrollToTop
+            nextTitle={'Older'}
+            prevTitle={'Newer'}
           />
         )}
     </div>
@@ -234,7 +292,7 @@ function IncomeNotificationGroupItem(props: {
         'relative cursor-pointer bg-white px-2 pt-6 text-sm',
         className,
         !expanded ? 'hover:bg-gray-100' : '',
-        highlighted && !expanded ? 'bg-indigo-200 hover:bg-indigo-100' : ''
+        highlighted && !expanded ? HIGHLIGHT_CLASS : ''
       )}
       onClick={onClickHandler}
     >
@@ -333,7 +391,7 @@ function IncomeNotificationItem(props: {
       reasonText = !simple
         ? `Bonus for ${
             parseInt(sourceText) / UNIQUE_BETTOR_BONUS_AMOUNT
-          } unique bettors`
+          } unique traders`
         : 'bonus on'
     } else if (sourceType === 'tip') {
       reasonText = !simple ? `tipped you` : `in tips on`
@@ -372,10 +430,14 @@ function IncomeNotificationItem(props: {
     <div
       className={clsx(
         'bg-white px-2 pt-6 text-sm sm:px-4',
-        highlighted && 'bg-indigo-200 hover:bg-indigo-100'
+        highlighted && HIGHLIGHT_CLASS
       )}
     >
-      <a href={getSourceUrl(notification)}>
+      <div className={'relative'}>
+        <SiteLink
+          href={getSourceUrl(notification) ?? ''}
+          className={'absolute left-0 right-0 top-0 bottom-0 z-0'}
+        />
         <Row className={'items-center text-gray-500 sm:justify-start'}>
           <div className={'line-clamp-2 flex max-w-xl shrink '}>
             <div className={'inline'}>
@@ -401,7 +463,7 @@ function IncomeNotificationItem(props: {
           </div>
         </Row>
         <div className={'mt-4 border-b border-gray-300'} />
-      </a>
+      </div>
     </div>
   )
 }
@@ -441,7 +503,7 @@ function NotificationGroupItem(props: {
         'relative cursor-pointer bg-white px-2 pt-6 text-sm',
         className,
         !expanded ? 'hover:bg-gray-100' : '',
-        highlighted && !expanded ? 'bg-indigo-200 hover:bg-indigo-100' : ''
+        highlighted && !expanded ? HIGHLIGHT_CLASS : ''
       )}
       onClick={onClickHandler}
     >
@@ -550,6 +612,8 @@ function NotificationItem(props: {
     setNotificationsAsSeen([notification])
   }, [notification])
 
+  const questionNeedsResolution = sourceUpdateType == 'closed'
+
   if (justSummary) {
     return (
       <Row className={'items-center text-sm text-gray-500 sm:justify-start'}>
@@ -565,7 +629,7 @@ function NotificationItem(props: {
               <span className={'flex-shrink-0'}>
                 {sourceType &&
                   reason &&
-                  getReasonForShowingNotification(notification, true, true)}
+                  getReasonForShowingNotification(notification, true)}
               </span>
               <div className={'ml-1 text-black'}>
                 <NotificationTextLabel
@@ -585,31 +649,39 @@ function NotificationItem(props: {
     <div
       className={clsx(
         'bg-white px-2 pt-6 text-sm sm:px-4',
-        highlighted && 'bg-indigo-200 hover:bg-indigo-100'
+        highlighted && HIGHLIGHT_CLASS
       )}
     >
-      <a
-        href={getSourceUrl(notification)}
-        onClick={() =>
-          track('Notification Clicked', {
-            type: 'notification item',
-            sourceType,
-            sourceUserName,
-            sourceUserAvatarUrl,
-            sourceUpdateType,
-            reasonText,
-            reason,
-            sourceUserUsername,
-            sourceText,
-          })
-        }
-      >
+      <div className={'relative cursor-pointer'}>
+        <SiteLink
+          href={getSourceUrl(notification) ?? ''}
+          className={'absolute left-0 right-0 top-0 bottom-0 z-0'}
+          onClick={() =>
+            track('Notification Clicked', {
+              type: 'notification item',
+              sourceType,
+              sourceUserName,
+              sourceUserAvatarUrl,
+              sourceUpdateType,
+              reasonText,
+              reason,
+              sourceUserUsername,
+              sourceText,
+            })
+          }
+        />
         <Row className={'items-center text-gray-500 sm:justify-start'}>
           <Avatar
-            avatarUrl={sourceUserAvatarUrl}
+            avatarUrl={
+              questionNeedsResolution
+                ? MANIFOLD_AVATAR_URL
+                : sourceUserAvatarUrl
+            }
             size={'sm'}
-            className={'mr-2'}
-            username={sourceUserName}
+            className={'z-10 mr-2'}
+            username={
+              questionNeedsResolution ? MANIFOLD_USERNAME : sourceUserUsername
+            }
           />
           <div className={'flex w-full flex-row pl-1 sm:pl-0'}>
             <div
@@ -618,18 +690,17 @@ function NotificationItem(props: {
               }
             >
               <div>
-                {sourceUpdateType != 'closed' && (
+                {!questionNeedsResolution && (
                   <UserLink
                     name={sourceUserName || ''}
                     username={sourceUserUsername || ''}
-                    className={'mr-1 flex-shrink-0'}
+                    className={'relative mr-1 flex-shrink-0'}
                     justFirstName={true}
                   />
                 )}
                 {getReasonForShowingNotification(
                   notification,
-                  false,
-                  isChildOfGroup
+                  isChildOfGroup ?? false
                 )}
                 {isChildOfGroup ? (
                   <RelativeTimestamp time={notification.createdTime} />
@@ -650,7 +721,7 @@ function NotificationItem(props: {
         </div>
 
         <div className={'mt-6 border-b border-gray-300'} />
-      </a>
+      </div>
     </div>
   )
 }
@@ -690,15 +761,17 @@ function QuestionOrGroupLink(props: {
       </span>
     )
   return (
-    <a
-      className={
-        'ml-1 font-bold hover:underline hover:decoration-indigo-400 hover:decoration-2 '
-      }
+    <SiteLink
+      className={'relative ml-1 font-bold'}
       href={
         sourceContractCreatorUsername
           ? `/${sourceContractCreatorUsername}/${sourceContractSlug}`
-          : (sourceType === 'group' || sourceType === 'tip') && sourceSlug
+          : // User's added to group or received a tip there
+          (sourceType === 'group' || sourceType === 'tip') && sourceSlug
           ? `${groupPath(sourceSlug)}`
+          : // User referral via group
+          sourceSlug?.includes('/group/')
+          ? `${sourceSlug}`
           : ''
       }
       onClick={() =>
@@ -714,7 +787,7 @@ function QuestionOrGroupLink(props: {
       }
     >
       {sourceContractTitle || sourceTitle}
-    </a>
+    </SiteLink>
   )
 }
 
@@ -729,12 +802,16 @@ function getSourceUrl(notification: Notification) {
   } = notification
   if (sourceType === 'follow') return `/${sourceUserUsername}`
   if (sourceType === 'group' && sourceSlug) return `${groupPath(sourceSlug)}`
+  // User referral via contract:
   if (
     sourceContractCreatorUsername &&
     sourceContractSlug &&
     sourceType === 'user'
   )
     return `/${sourceContractCreatorUsername}/${sourceContractSlug}`
+  // User referral:
+  if (sourceType === 'user' && !sourceContractSlug)
+    return `/${sourceUserUsername}`
   if (sourceType === 'tip' && sourceContractSlug)
     return `/${sourceContractCreatorUsername}/${sourceContractSlug}#${sourceSlug}`
   if (sourceType === 'tip' && sourceSlug) return `${groupPath(sourceSlug)}`
@@ -769,17 +846,10 @@ function NotificationTextLabel(props: {
   justSummary?: boolean
 }) {
   const { className, notification, justSummary } = props
-  const {
-    sourceUpdateType,
-    sourceType,
-    sourceText,
-    sourceContractTitle,
-    reasonText,
-  } = notification
+  const { sourceUpdateType, sourceType, sourceText, reasonText } = notification
   const defaultText = sourceText ?? reasonText ?? ''
   if (sourceType === 'contract') {
-    if (justSummary) return <span>{sourceContractTitle}</span>
-    if (!sourceText) return <div />
+    if (justSummary || !sourceText) return <div />
     // Resolved contracts
     if (sourceType === 'contract' && sourceUpdateType === 'resolved') {
       {
@@ -857,27 +927,27 @@ function NotificationTextLabel(props: {
 
 function getReasonForShowingNotification(
   notification: Notification,
-  simple?: boolean,
-  replaceOn?: boolean
+  justSummary: boolean
 ) {
   const { sourceType, sourceUpdateType, reason, sourceSlug } = notification
   let reasonText: string
   switch (sourceType) {
     case 'comment':
       if (reason === 'reply_to_users_answer')
-        reasonText = !simple ? 'replied to you on' : 'replied'
+        reasonText = justSummary ? 'replied' : 'replied to you on'
       else if (reason === 'tagged_user')
-        reasonText = !simple ? 'tagged you on' : 'tagged you'
+        reasonText = justSummary ? 'tagged you' : 'tagged you on'
       else if (reason === 'reply_to_users_comment')
-        reasonText = !simple ? 'replied to you on' : 'replied'
-      else reasonText = `commented on`
+        reasonText = justSummary ? 'replied' : 'replied to you on'
+      else reasonText = justSummary ? `commented` : `commented on`
       break
     case 'contract':
-      if (reason === 'you_follow_user') reasonText = 'asked'
-      else if (sourceUpdateType === 'resolved') reasonText = `resolved`
-      else if (sourceUpdateType === 'closed')
-        reasonText = `Please resolve your question`
-      else reasonText = `updated`
+      if (reason === 'you_follow_user')
+        reasonText = justSummary ? 'asked the question' : 'asked'
+      else if (sourceUpdateType === 'resolved')
+        reasonText = justSummary ? `resolved the question` : `resolved`
+      else if (sourceUpdateType === 'closed') reasonText = `Please resolve`
+      else reasonText = justSummary ? 'updated the question' : `updated`
       break
     case 'answer':
       if (reason === 'on_users_contract') reasonText = `answered your question `
@@ -904,205 +974,5 @@ function getReasonForShowingNotification(
     default:
       reasonText = ''
   }
-  return replaceOn ? reasonText.replace(' on', '') : reasonText
-}
-
-// TODO: where should we put referral bonus notifications?
-function NotificationSettings() {
-  const user = useUser()
-  const [notificationSettings, setNotificationSettings] =
-    useState<notification_subscribe_types>('all')
-  const [emailNotificationSettings, setEmailNotificationSettings] =
-    useState<notification_subscribe_types>('all')
-  const [privateUser, setPrivateUser] = useState<PrivateUser | null>(null)
-
-  useEffect(() => {
-    if (user) listenForPrivateUser(user.id, setPrivateUser)
-  }, [user])
-
-  useEffect(() => {
-    if (!privateUser) return
-    if (privateUser.notificationPreferences) {
-      setNotificationSettings(privateUser.notificationPreferences)
-    }
-    if (
-      privateUser.unsubscribedFromResolutionEmails &&
-      privateUser.unsubscribedFromCommentEmails &&
-      privateUser.unsubscribedFromAnswerEmails
-    ) {
-      setEmailNotificationSettings('none')
-    } else if (
-      !privateUser.unsubscribedFromResolutionEmails &&
-      !privateUser.unsubscribedFromCommentEmails &&
-      !privateUser.unsubscribedFromAnswerEmails
-    ) {
-      setEmailNotificationSettings('all')
-    } else {
-      setEmailNotificationSettings('less')
-    }
-  }, [privateUser])
-
-  const loading = 'Changing Notifications Settings'
-  const success = 'Notification Settings Changed!'
-  function changeEmailNotifications(newValue: notification_subscribe_types) {
-    if (!privateUser) return
-    if (newValue === 'all') {
-      toast.promise(
-        updatePrivateUser(privateUser.id, {
-          unsubscribedFromResolutionEmails: false,
-          unsubscribedFromCommentEmails: false,
-          unsubscribedFromAnswerEmails: false,
-        }),
-        {
-          loading,
-          success,
-          error: (err) => `${err.message}`,
-        }
-      )
-    } else if (newValue === 'less') {
-      toast.promise(
-        updatePrivateUser(privateUser.id, {
-          unsubscribedFromResolutionEmails: false,
-          unsubscribedFromCommentEmails: true,
-          unsubscribedFromAnswerEmails: true,
-        }),
-        {
-          loading,
-          success,
-          error: (err) => `${err.message}`,
-        }
-      )
-    } else if (newValue === 'none') {
-      toast.promise(
-        updatePrivateUser(privateUser.id, {
-          unsubscribedFromResolutionEmails: true,
-          unsubscribedFromCommentEmails: true,
-          unsubscribedFromAnswerEmails: true,
-        }),
-        {
-          loading,
-          success,
-          error: (err) => `${err.message}`,
-        }
-      )
-    }
-  }
-
-  function changeInAppNotificationSettings(
-    newValue: notification_subscribe_types
-  ) {
-    if (!privateUser) return
-    track('In-App Notification Preferences Changed', {
-      newPreference: newValue,
-      oldPreference: privateUser.notificationPreferences,
-    })
-    toast.promise(
-      updatePrivateUser(privateUser.id, {
-        notificationPreferences: newValue,
-      }),
-      {
-        loading,
-        success,
-        error: (err) => `${err.message}`,
-      }
-    )
-  }
-
-  useEffect(() => {
-    if (privateUser && privateUser.notificationPreferences)
-      setNotificationSettings(privateUser.notificationPreferences)
-    else setNotificationSettings('all')
-  }, [privateUser])
-
-  if (!privateUser) {
-    return <LoadingIndicator spinnerClassName={'border-gray-500 h-4 w-4'} />
-  }
-
-  function NotificationSettingLine(props: {
-    label: string
-    highlight: boolean
-  }) {
-    const { label, highlight } = props
-    return (
-      <Row className={clsx('my-1 text-gray-300', highlight && '!text-black')}>
-        {highlight ? <CheckIcon height={20} /> : <XIcon height={20} />}
-        {label}
-      </Row>
-    )
-  }
-
-  return (
-    <div className={'p-2'}>
-      <div>In App Notifications</div>
-      <ChoicesToggleGroup
-        currentChoice={notificationSettings}
-        choicesMap={{ All: 'all', Less: 'less', None: 'none' }}
-        setChoice={(choice) =>
-          changeInAppNotificationSettings(
-            choice as notification_subscribe_types
-          )
-        }
-        className={'col-span-4 p-2'}
-        toggleClassName={'w-24'}
-      />
-      <div className={'mt-4 text-sm'}>
-        <div>
-          <div className={''}>
-            You will receive notifications for:
-            <NotificationSettingLine
-              label={"Resolution of questions you've interacted with"}
-              highlight={notificationSettings !== 'none'}
-            />
-            <NotificationSettingLine
-              highlight={notificationSettings !== 'none'}
-              label={'Activity on your own questions, comments, & answers'}
-            />
-            <NotificationSettingLine
-              highlight={notificationSettings !== 'none'}
-              label={"Activity on questions you're betting on"}
-            />
-            <NotificationSettingLine
-              highlight={notificationSettings !== 'none'}
-              label={"Income & referral bonuses you've received"}
-            />
-            <NotificationSettingLine
-              label={"Activity on questions you've ever bet or commented on"}
-              highlight={notificationSettings === 'all'}
-            />
-          </div>
-        </div>
-      </div>
-      <div className={'mt-4'}>Email Notifications</div>
-      <ChoicesToggleGroup
-        currentChoice={emailNotificationSettings}
-        choicesMap={{ All: 'all', Less: 'less', None: 'none' }}
-        setChoice={(choice) =>
-          changeEmailNotifications(choice as notification_subscribe_types)
-        }
-        className={'col-span-4 p-2'}
-        toggleClassName={'w-24'}
-      />
-      <div className={'mt-4 text-sm'}>
-        <div>
-          You will receive emails for:
-          <NotificationSettingLine
-            label={"Resolution of questions you're betting on"}
-            highlight={emailNotificationSettings !== 'none'}
-          />
-          <NotificationSettingLine
-            label={'Closure of your questions'}
-            highlight={emailNotificationSettings !== 'none'}
-          />
-          <NotificationSettingLine
-            label={'Activity on your questions'}
-            highlight={emailNotificationSettings === 'all'}
-          />
-          <NotificationSettingLine
-            label={"Activity on questions you've answered or commented on"}
-            highlight={emailNotificationSettings === 'all'}
-          />
-        </div>
-      </div>
-    </div>
-  )
+  return reasonText
 }
diff --git a/web/pages/profile.tsx b/web/pages/profile.tsx
index b80698ae..541f5de9 100644
--- a/web/pages/profile.tsx
+++ b/web/pages/profile.tsx
@@ -1,6 +1,5 @@
 import React, { useEffect, useState } from 'react'
 import { RefreshIcon } from '@heroicons/react/outline'
-import Router from 'next/router'
 
 import { AddFundsButton } from 'web/components/add-funds-button'
 import { Page } from 'web/components/page'
@@ -18,6 +17,9 @@ import { updateUser, updatePrivateUser } from 'web/lib/firebase/users'
 import { defaultBannerUrl } from 'web/components/user-page'
 import { SiteLink } from 'web/components/site-link'
 import Textarea from 'react-expanding-textarea'
+import { redirectIfLoggedOut } from 'web/lib/firebase/server-auth'
+
+export const getServerSideProps = redirectIfLoggedOut('/')
 
 function EditUserField(props: {
   user: User
@@ -134,8 +136,7 @@ export default function ProfilePage() {
       })
   }
 
-  if (user === null) {
-    Router.replace('/')
+  if (user == null) {
     return <></>
   }
 
diff --git a/web/pages/referrals.tsx b/web/pages/referrals.tsx
new file mode 100644
index 00000000..f50c2e2b
--- /dev/null
+++ b/web/pages/referrals.tsx
@@ -0,0 +1,62 @@
+import { Col } from 'web/components/layout/col'
+import { SEO } from 'web/components/SEO'
+import { Title } from 'web/components/title'
+import { useUser } from 'web/hooks/use-user'
+import { Page } from 'web/components/page'
+import { useTracking } from 'web/hooks/use-tracking'
+import { redirectIfLoggedOut } from 'web/lib/firebase/server-auth'
+import { REFERRAL_AMOUNT } from 'common/user'
+import { CopyLinkButton } from 'web/components/copy-link-button'
+import { ENV_CONFIG } from 'common/envs/constants'
+import { InfoBox } from 'web/components/info-box'
+
+export const getServerSideProps = redirectIfLoggedOut('/')
+
+export default function ReferralsPage() {
+  const user = useUser()
+
+  useTracking('view referrals')
+
+  const url = `https://${ENV_CONFIG.domain}?referrer=${user?.username}`
+
+  return (
+    <Page>
+      <SEO
+        title="Referrals"
+        description={`Manifold's referral program. Invite new users to Manifold and get M${REFERRAL_AMOUNT} if they
+            sign up!`}
+        url="/referrals"
+      />
+
+      <Col className="items-center">
+        <Col className="h-full rounded bg-white p-4 py-8 sm:p-8 sm:shadow-md">
+          <Title className="!mt-0" text="Referrals" />
+          <img
+            className="mb-6 block -scale-x-100 self-center"
+            src="/logo-flapping-with-money.gif"
+            width={200}
+            height={200}
+          />
+
+          <div className={'mb-4'}>
+            Invite new users to Manifold and get M${REFERRAL_AMOUNT} if they
+            sign up!
+          </div>
+
+          <CopyLinkButton
+            url={url}
+            tracking="copy referral link"
+            buttonClassName="btn-md rounded-l-none"
+            toastClassName={'-left-28 mt-1'}
+          />
+
+          <InfoBox
+            title="FYI"
+            className="mt-4 max-w-md"
+            text="You can also earn the referral bonus from sharing the link to any market or group you've created!"
+          />
+        </Col>
+      </Col>
+    </Page>
+  )
+}
diff --git a/web/pages/trades.tsx b/web/pages/trades.tsx
index 55a08bc6..a29fb7f0 100644
--- a/web/pages/trades.tsx
+++ b/web/pages/trades.tsx
@@ -1,17 +1,10 @@
 import Router from 'next/router'
-import { useEffect } from 'react'
+import { redirectIfLoggedOut } from 'web/lib/firebase/server-auth'
 
-import { useUser } from 'web/hooks/use-user'
+export const getServerSideProps = redirectIfLoggedOut('/')
 
 // Deprecated: redirects to /portfolio.
 // Eventually, this will be removed.
 export default function TradesPage() {
-  const user = useUser()
-
-  useEffect(() => {
-    if (user === null) Router.replace('/')
-    else Router.replace('/portfolio')
-  })
-
-  return <></>
+  Router.replace('/portfolio')
 }
diff --git a/web/public/logo-flapping-with-money.gif b/web/public/logo-flapping-with-money.gif
new file mode 100644
index 00000000..0ef936a4
Binary files /dev/null and b/web/public/logo-flapping-with-money.gif differ
diff --git a/web/public/mtg/app.js b/web/public/mtg/app.js
new file mode 100644
index 00000000..fc7711d0
--- /dev/null
+++ b/web/public/mtg/app.js
@@ -0,0 +1,354 @@
+mode = 'PLAY'
+allData = {}
+total = 0
+unseenTotal = 0
+probList = []
+nameList = []
+k = 12
+extra = 3
+artDict = {}
+totalCorrect = 0
+totalSeen = 0
+wordsLeft = k + extra
+imagesLeft = k
+maxRounds = 20
+whichGuesser = 'counterspell'
+un = false
+online = false
+firstPrint = false
+flag = true
+page = 1
+
+document.location.search.split('&').forEach((pair) => {
+  let v = pair.split('=')
+  if (v[0] === '?whichguesser') {
+    whichGuesser = v[1]
+  } else if (v[0] === 'un') {
+    un = v[1]
+  } else if (v[0] === 'digital') {
+    online = v[1]
+  } else if (v[0] === 'original') {
+    firstPrint = v[1]
+  }
+})
+
+let firstFetch = fetch('jsons/' + whichGuesser + page + '.json')
+fetchToResponse(firstFetch)
+
+function putIntoMapAndFetch(data) {
+  putIntoMap(data.data)
+  if (data.has_more) {
+    page += 1
+    window.setTimeout(() =>
+      fetchToResponse(fetch('jsons/' + whichGuesser + page + '.json'))
+    )
+  } else {
+    for (const [key, value] of Object.entries(allData)) {
+      nameList.push(key)
+      probList.push(
+        value.length +
+          (probList.length === 0 ? 0 : probList[probList.length - 1])
+      )
+      unseenTotal = total
+    }
+    window.console.log(allData)
+    window.console.log(total)
+    window.console.log(probList)
+    window.console.log(nameList)
+    if (whichGuesser === 'counterspell') {
+      document.getElementById('guess-type').innerText = 'Counterspell Guesser'
+    } else if (whichGuesser === 'burn') {
+      document.getElementById('guess-type').innerText = 'Match With Hot Singles'
+    }
+    setUpNewGame()
+  }
+}
+
+function getKSamples() {
+  let usedCounters = new Set()
+  let currentTotal = unseenTotal
+  let samples = {}
+  let i = 0
+  while (i < k) {
+    let rand = Math.floor(Math.random() * currentTotal)
+    let count = 0
+    for (const [key, value] of Object.entries(allData)) {
+      if (usedCounters.has(key)) {
+        continue
+      } else if (count >= rand) {
+        usedCounters.add(key)
+        currentTotal -= value.length
+        unseenTotal--
+        let randIndex = Math.floor(Math.random() * value.length)
+        let arts = allData[key].splice(randIndex, 1)
+        samples[arts[0].artImg] = [key, arts[0].normalImg]
+        i++
+        break
+      } else {
+        count += value.length
+      }
+    }
+  }
+  for (const key of usedCounters) {
+    if (allData[key].length === 0) {
+      delete allData[key]
+    }
+  }
+  let count = 0
+  while (count < extra) {
+    let rand = Math.floor(Math.random() * total)
+    for (let j = 0; j < nameList.length; j++) {
+      if (j >= rand) {
+        if (usedCounters.has(nameList[j])) {
+          break
+        }
+        usedCounters.add(nameList[j])
+        count += 1
+        break
+      }
+    }
+  }
+  return [samples, usedCounters]
+}
+
+function fetchToResponse(fetch) {
+  return fetch
+    .then((response) => response.json())
+    .then((json) => {
+      putIntoMapAndFetch(json)
+    })
+}
+
+function determineIfSkip(card) {
+  if (!un) {
+    if (card.set_type === 'funny') {
+      return true
+    }
+  }
+  if (!online) {
+    if (card.digital) {
+      return true
+    }
+  }
+  if (firstPrint) {
+    if (
+      card.reprint === true ||
+      (card.frame_effects && card.frame_effects.includes('showcase'))
+    ) {
+      return true
+    }
+  }
+  // reskinned card names show in art crop
+  if (card.flavor_name) {
+    return true
+  }
+  // don't include racist cards
+  return card.content_warning
+}
+
+function putIntoMap(data) {
+  for (let i = 0; i < data.length; i++) {
+    let card = data[i]
+    if (determineIfSkip(card)) {
+      continue
+    }
+    let name = card.name
+    // remove slashes from adventure cards
+    if (card.card_faces) {
+      name = card.card_faces[0].name
+    }
+    let normalImg = ''
+    if (card.image_uris.normal) {
+      normalImg = card.image_uris.normal
+    } else if (card.image_uris.large) {
+      normalImg = card.image_uris.large
+    } else if (card.image_uris.small) {
+      normalImg = card.image_uris.small
+    } else {
+      continue
+    }
+    let artImg = ''
+    if (card.image_uris.art_crop) {
+      artImg = card.image_uris.art_crop
+    } else {
+      continue
+    }
+    total += 1
+    if (!allData[name]) {
+      allData[name] = [{ artImg: artImg, normalImg: normalImg }]
+    } else {
+      allData[name].push({ artImg: artImg, normalImg: normalImg })
+    }
+  }
+}
+
+function shuffleArray(array) {
+  for (let i = array.length - 1; i > 0; i--) {
+    let j = Math.floor(Math.random() * (i + 1))
+    let temp = array[i]
+    array[i] = array[j]
+    array[j] = temp
+  }
+}
+
+function setUpNewGame() {
+  wordsLeft = k + extra
+  imagesLeft = k
+  let currentRound = totalSeen / k
+  if (currentRound + 1 === maxRounds) {
+    document.getElementById('round-number').innerText = 'Final Round'
+  } else {
+    document.getElementById('round-number').innerText =
+      'Round ' + (1 + currentRound)
+  }
+
+  setWordsLeft()
+  // select new cards
+  let sampledData = getKSamples()
+  artDict = sampledData[0]
+  let randomImages = Object.keys(artDict)
+  shuffleArray(randomImages)
+  let namesList = Array.from(sampledData[1]).sort()
+  // fill in the new cards and names
+  for (let cardIndex = 1; cardIndex <= k; cardIndex++) {
+    let currCard = document.getElementById('card-' + cardIndex)
+    currCard.classList.remove('incorrect')
+    currCard.dataset.name = ''
+    currCard.dataset.url = randomImages[cardIndex - 1]
+    currCard.style.backgroundImage = "url('" + currCard.dataset.url + "')"
+  }
+  const nameBank = document.querySelector('.names-bank')
+  for (nameIndex = 1; nameIndex <= k + extra; nameIndex++) {
+    currName = document.getElementById('name-' + nameIndex)
+    // window.console.log(currName)
+    currName.innerText = namesList[nameIndex - 1]
+    nameBank.appendChild(currName)
+  }
+}
+
+function checkAnswers() {
+  let score = k
+  // show the correct full cards
+  for (cardIndex = 1; cardIndex <= k; cardIndex++) {
+    currCard = document.getElementById('card-' + cardIndex)
+    let incorrect = true
+    if (currCard.dataset.name) {
+      let guess = document.getElementById(currCard.dataset.name).innerText
+      // window.console.log(artDict[currCard.dataset.url][0], guess);
+      incorrect = artDict[currCard.dataset.url][0] !== guess
+      // decide if their guess was correct
+    }
+    if (incorrect) currCard.classList.add('incorrect')
+    // tally some kind of score
+    if (incorrect) score--
+    // show the correct card
+    currCard.style.backgroundImage =
+      "url('" + artDict[currCard.dataset.url][1] + "')"
+  }
+  totalSeen += k
+  totalCorrect += score
+  document.getElementById('score-amount').innerText = score + '/' + k
+  document.getElementById('score-percent').innerText = Math.round(
+    (totalCorrect * 100) / totalSeen
+  )
+  document.getElementById('score-amount-total').innerText =
+    totalCorrect + '/' + totalSeen
+}
+
+function toggleMode() {
+  event.preventDefault()
+  if (mode === 'PLAY') {
+    mode = 'ANSWER'
+    document.querySelector('.play-page').classList.add('answer-page')
+    window.console.log(totalSeen)
+    if (totalSeen / k === maxRounds - 1) {
+      document.getElementById('submit').style.display = 'none'
+    } else {
+      document.getElementById('submit').value = 'Next Round'
+    }
+    checkAnswers()
+  } else {
+    mode = 'PLAY'
+    document.querySelector('.play-page').classList.remove('answer-page')
+    document.getElementById('submit').value = 'Submit'
+    setUpNewGame()
+  }
+}
+
+function allowDrop(ev, id) {
+  ev.preventDefault()
+}
+
+function drag(ev) {
+  ev.dataTransfer.setData('text', ev.target.id)
+  let nameEl = document.querySelector('.selected')
+  if (nameEl) nameEl.classList.remove('selected')
+}
+
+function drop(ev, id) {
+  ev.preventDefault()
+  var data = ev.dataTransfer.getData('text')
+  dropOnCard(id, data)
+}
+
+function returnDrop(ev) {
+  ev.preventDefault()
+  var data = ev.dataTransfer.getData('text')
+  returnToNameBank(data)
+}
+
+function returnToNameBank(name) {
+  document
+    .querySelector('.names-bank')
+    .appendChild(document.getElementById(name))
+  let prevContainer = document.querySelector('[data-name=' + name + ']')
+  if (prevContainer) {
+    prevContainer.dataset.name = ''
+    wordsLeft += 1
+    imagesLeft += 1
+    setWordsLeft()
+  }
+}
+
+function selectName(ev) {
+  if (ev.target.parentNode.classList.contains('names-bank')) {
+    let nameEl = document.querySelector('.selected')
+    if (nameEl) nameEl.classList.remove('selected')
+    ev.target.classList.add('selected')
+  } else {
+    returnToNameBank(ev.target.id)
+  }
+}
+
+function dropSelected(ev, id) {
+  ev.preventDefault()
+  let nameEl = document.querySelector('.selected')
+  window.console.log('drop selected', nameEl)
+  if (!nameEl) return
+  nameEl.classList.remove('selected')
+  dropOnCard(id, nameEl.id)
+}
+
+function dropOnCard(id, data) {
+  let target = document.getElementById('card-' + id)
+  target.appendChild(document.getElementById(data))
+  // if this already has a name, remove that name
+  if (target.dataset.name) {
+    returnToNameBank(target.dataset.name)
+  }
+  // remove name data from a previous card if there is one
+  let prevContainer = document.querySelector('[data-name=' + data + ']')
+  if (prevContainer) {
+    prevContainer.dataset.name = ''
+  } else {
+    wordsLeft -= 1
+    imagesLeft -= 1
+    setWordsLeft()
+  }
+  target.dataset.name = data
+}
+
+function setWordsLeft() {
+  document.getElementById('words-left').innerText =
+    'Unused Card Names: ' + wordsLeft + '/Images: ' + imagesLeft
+}
diff --git a/web/public/mtg/guess.html b/web/public/mtg/guess.html
new file mode 100644
index 00000000..882883a7
--- /dev/null
+++ b/web/public/mtg/guess.html
@@ -0,0 +1,559 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <!-- Google Tag Manager -->
+    <script>
+      ;(function (w, d, s, l, i) {
+        w[l] = w[l] || []
+        w[l].push({
+          'gtm.start': new Date().getTime(),
+          event: 'gtm.js',
+        })
+        var f = d.getElementsByTagName(s)[0],
+          j = d.createElement(s),
+          dl = l !== 'dataLayer' ? '&l=' + l : ''
+        j.async = true
+        j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl
+        f.parentNode.insertBefore(j, f)
+      })(window, document, 'script', 'dataLayer', 'GTM-M3MBVGG')
+    </script>
+    <!-- End Google Tag Manager -->
+    <meta charset="UTF-8" />
+    <script type="text/javascript" src="app.js"></script>
+    <style type="text/css">
+      body {
+        position: relative;
+      }
+
+      .play-page {
+        display: flex;
+        flex-direction: row-reverse;
+        font-family: Georgia, 'Times New Roman', Times, serif;
+      }
+
+      h1 {
+        font-family: Verdana, Geneva, Tahoma, sans-serif;
+        text-align: center;
+      }
+
+      form {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        margin-right: 240px;
+      }
+
+      .cards-container {
+        display: flex;
+        flex-wrap: wrap;
+        flex-direction: row;
+        justify-content: center;
+      }
+
+      .card {
+        width: 230px;
+        height: 208px;
+        border: 5px solid lightgrey;
+        margin: 5px;
+        align-items: flex-end;
+        box-sizing: border-box;
+        border-radius: 11px;
+        position: relative;
+        display: flex;
+        justify-content: center;
+        /*background-size: contain;*/
+        background-size: 220px;
+        background-repeat: no-repeat;
+        transition: height 1s, background-image 1s, border 0.4s 0.6s;
+        background-position-y: calc(50% - 18px);
+      }
+
+      .card:not([data-name^='name'])::after {
+        content: '';
+        height: 34px;
+        background: white;
+        width: 100%;
+      }
+
+      .answer-page .card {
+        height: 350px;
+        /*padding-top: 310px;*/
+        /*background-size: cover;*/
+        overflow: hidden;
+        border-color: rgb(0, 146, 156);
+      }
+
+      .answer-page .card.incorrect {
+        border-color: rgb(216, 27, 96);
+      }
+
+      .names-bank {
+        position: fixed;
+        padding: 10px 10px 40px;
+      }
+
+      .names-bank .name {
+        margin: 6px 0;
+      }
+
+      .answer-page .names-bank .name {
+        display: none;
+      }
+
+      .answer-page .names-bank .word-count {
+        display: none;
+      }
+
+      .word-count {
+        text-align: center;
+        font-style: italic;
+        color: #444;
+      }
+
+      .score {
+        width: 100%;
+        text-align: center;
+        background-color: rgb(255, 193, 7);
+        width: 200px;
+        font-family: Verdana, Geneva, Tahoma, sans-serif;
+        opacity: 0;
+      }
+
+      .names-bank .score {
+        overflow: hidden;
+        height: 0;
+      }
+
+      .answer-page .names-bank .score {
+        height: auto;
+        display: block;
+        opacity: 1;
+        transition: opacity 1.2s 0.2s;
+        padding: 20px;
+      }
+
+      .name {
+        width: 230px;
+        min-height: 36px;
+        border-radius: 2px;
+        background-color: lightgrey;
+        padding: 8px 12px 2px;
+        box-sizing: border-box;
+      }
+
+      .card .name {
+        border-radius: 0 0 5px 5px;
+      }
+
+      #submit {
+        margin-top: 10px;
+        padding: 8px 20px;
+        background-color: cadetblue;
+        border: none;
+        border-radius: 3px;
+        font-size: 1.1em;
+        color: white;
+        cursor: pointer;
+      }
+
+      #submit:hover {
+        background-color: rgb(0, 146, 156);
+      }
+
+      #newGame {
+        padding: 8px 20px;
+        background-color: lightpink;
+        border: none;
+        position: absolute;
+        top: 5px;
+        left: 20px;
+        border-radius: 3px;
+        font-size: 0.7em;
+        cursor: pointer;
+      }
+
+      #newGame:hover {
+        background-color: coral;
+      }
+
+      .selected {
+        background-color: orange;
+      }
+
+      @media screen and (orientation: landscape) and (max-height: 680px) {
+        /* CSS applied when the device is in landscape mode*/
+        .names-bank {
+          padding: 0;
+          top: 0;
+          max-height: 100vh;
+          overflow: scroll;
+        }
+
+        body {
+          font-size: 20px;
+        }
+
+        .word-count {
+          font-size: 14px;
+        }
+
+        h1 {
+          margin-right: 240px;
+        }
+      }
+
+      @media screen and (orientation: portrait) and (max-width: 1100px) {
+        body {
+          font-size: 1.8em;
+        }
+
+        .play-page {
+          flex-direction: column;
+        }
+
+        .names-bank {
+          flex-direction: row;
+          display: flex;
+          flex-wrap: wrap;
+          /* position: fixed; */
+          padding: 10px 10px 40px;
+          position: sticky;
+          top: 0;
+          z-index: 100;
+          background: white;
+        }
+
+        .answer-page .names-bank {
+          min-width: 100%;
+          justify-content: center;
+        }
+
+        form {
+          margin: 0;
+        }
+
+        .names-bank .name {
+          margin: 6px;
+        }
+
+        .names-bank .score {
+          width: 0;
+        }
+
+        .answer-page .names-bank .score {
+          width: auto;
+        }
+
+        .word-count {
+          position: absolute;
+          margin-top: -20px;
+        }
+
+        .name {
+          width: 300px;
+        }
+
+        .card {
+          width: 300px;
+          background-size: 300px;
+          height: 266px;
+        }
+
+        .answer-page .card {
+          height: 454px;
+        }
+      }
+    </style>
+  </head>
+  <body>
+    <!-- Google Tag Manager (noscript) -->
+    <noscript>
+      <iframe
+        src="https://www.googletagmanager.com/ns.html?id=GTM-M3MBVGG"
+        height="0"
+        width="0"
+        style="display: none; visibility: hidden"
+      ></iframe>
+    </noscript>
+    <!-- End Google Tag Manager (noscript) -->
+
+    <h1><span id="guess-type"></span>: <span id="round-number"></span></h1>
+
+    <div class="play-page">
+      <div
+        class="names-bank"
+        ondrop="returnDrop(event)"
+        ondragover="event.preventDefault()"
+      >
+        <div class="score">
+          YOUR SCORE
+          <div>Correct Answers This Round: <span id="score-amount"></span></div>
+          <div>
+            Correct Answers In Total: <span id="score-amount-total"></span>
+          </div>
+          <div>Overall Percent: <span id="score-percent"></span>%</div>
+        </div>
+        <div class="word-count"><span id="words-left"></span></div>
+        <div
+          class="name"
+          draggable="true"
+          ondragstart="drag(event)"
+          onClick="selectName(event)"
+          id="name-1"
+        >
+          Name 1
+        </div>
+        <div
+          class="name"
+          draggable="true"
+          ondragstart="drag(event)"
+          onClick="selectName(event)"
+          id="name-2"
+        >
+          Name 2
+        </div>
+        <div
+          class="name"
+          draggable="true"
+          ondragstart="drag(event)"
+          onClick="selectName(event)"
+          id="name-3"
+        >
+          Name 3
+        </div>
+        <div
+          class="name"
+          draggable="true"
+          ondragstart="drag(event)"
+          onClick="selectName(event)"
+          id="name-4"
+        >
+          Name 4
+        </div>
+        <div
+          class="name"
+          draggable="true"
+          ondragstart="drag(event)"
+          onClick="selectName(event)"
+          id="name-5"
+        >
+          Name 5
+        </div>
+        <div
+          class="name"
+          draggable="true"
+          ondragstart="drag(event)"
+          onClick="selectName(event)"
+          id="name-6"
+        >
+          Name 6
+        </div>
+        <div
+          class="name"
+          draggable="true"
+          ondragstart="drag(event)"
+          onClick="selectName(event)"
+          id="name-7"
+        >
+          Name 7
+        </div>
+        <div
+          class="name"
+          draggable="true"
+          ondragstart="drag(event)"
+          onClick="selectName(event)"
+          id="name-8"
+        >
+          Name 8
+        </div>
+        <div
+          class="name"
+          draggable="true"
+          ondragstart="drag(event)"
+          onClick="selectName(event)"
+          id="name-9"
+        >
+          Name 9
+        </div>
+        <div
+          class="name"
+          draggable="true"
+          ondragstart="drag(event)"
+          onClick="selectName(event)"
+          id="name-10"
+        >
+          Name 10
+        </div>
+        <div
+          class="name"
+          draggable="true"
+          ondragstart="drag(event)"
+          onClick="selectName(event)"
+          id="name-11"
+        >
+          Name 11
+        </div>
+        <div
+          class="name"
+          draggable="true"
+          ondragstart="drag(event)"
+          onClick="selectName(event)"
+          id="name-12"
+        >
+          Name 12
+        </div>
+        <div
+          class="name"
+          draggable="true"
+          ondragstart="drag(event)"
+          onClick="selectName(event)"
+          id="name-13"
+        >
+          Name 13
+        </div>
+        <div
+          class="name"
+          draggable="true"
+          ondragstart="drag(event)"
+          onClick="selectName(event)"
+          id="name-14"
+        >
+          Name 14
+        </div>
+        <div
+          class="name"
+          draggable="true"
+          ondragstart="drag(event)"
+          onClick="selectName(event)"
+          id="name-15"
+        >
+          Name 15
+        </div>
+      </div>
+      <form onsubmit="toggleMode(event)">
+        <div class="cards-container">
+          <div
+            class="card"
+            ondrop="drop(event,1)"
+            ondragover="allowDrop(event,1)"
+            onclick="dropSelected(event, 1)"
+            id="card-1"
+          ></div>
+          <div
+            class="card"
+            ondrop="drop(event,2)"
+            ondragover="allowDrop(event,2)"
+            onclick="dropSelected(event, 2)"
+            id="card-2"
+          ></div>
+          <div
+            class="card"
+            ondrop="drop(event,3)"
+            ondragover="allowDrop(event,3)"
+            onclick="dropSelected(event, 3)"
+            id="card-3"
+          ></div>
+          <div
+            class="card"
+            ondrop="drop(event,4)"
+            ondragover="allowDrop(event,4)"
+            onclick="dropSelected(event, 4)"
+            id="card-4"
+          ></div>
+          <div
+            class="card"
+            ondrop="drop(event,5)"
+            ondragover="allowDrop(event,5)"
+            onclick="dropSelected(event, 5)"
+            id="card-5"
+          ></div>
+          <div
+            class="card"
+            ondrop="drop(event, 6)"
+            ondragover="allowDrop(event,6)"
+            onclick="dropSelected(event,6)"
+            id="card-6"
+          ></div>
+          <div
+            class="card"
+            ondrop="drop(event,7)"
+            ondragover="allowDrop(event,7)"
+            onclick="dropSelected(event, 7)"
+            id="card-7"
+          ></div>
+          <div
+            class="card"
+            ondrop="drop(event,8)"
+            ondragover="allowDrop(event,8)"
+            onclick="dropSelected(event, 8)"
+            id="card-8"
+          ></div>
+          <div
+            class="card"
+            ondrop="drop(event,9)"
+            ondragover="allowDrop(event,9)"
+            onclick="dropSelected(event, 9)"
+            id="card-9"
+          ></div>
+          <div
+            class="card"
+            ondrop="drop(event,10)"
+            ondragover="allowDrop(event,10)"
+            onclick="dropSelected(event, 10)"
+            id="card-10"
+          ></div>
+          <div
+            class="card"
+            ondrop="drop(event,11)"
+            ondragover="allowDrop(event,11)"
+            onclick="dropSelected(event, 11)"
+            id="card-11"
+          ></div>
+          <div
+            class="card"
+            ondrop="drop(event,12)"
+            ondragover="allowDrop(event,12)"
+            onclick="dropSelected(event, 12)"
+            id="card-12"
+          ></div>
+        </div>
+        <input type="submit" id="submit" value="Submit" />
+      </form>
+    </div>
+
+    <div style="position: absolute; top: 0; left: 0; right: 0; color: grey">
+      <form method="get" action="index.html">
+        <input type="submit" id="newGame" value="New Game" />
+      </form>
+    </div>
+    <div style="margin: -40px 0 0; height: 60px">
+      <a href="https://paypal.me/idamayer">Donate, buy us a boba 🧋</a>
+    </div>
+
+    <div
+      style="
+        font-size: 0.9em;
+        position: absolute;
+        bottom: 0;
+        left: 0;
+        right: 0;
+        color: grey;
+        font-style: italic;
+      "
+    >
+      made by
+      <a
+        style="color: rgb(0, 146, 156); font-style: italic"
+        href="https://idamayer.com"
+        >Ida Mayer</a
+      >
+      &
+      <a
+        style="color: rgb(0, 146, 156); font-style: italic"
+        href="mailto:alexlien.alien@gmail.com"
+        >Alex Lien</a
+      >, 2022
+    </div>
+  </body>
+</html>
diff --git a/web/public/mtg/importCards.py b/web/public/mtg/importCards.py
new file mode 100644
index 00000000..343cba1a
--- /dev/null
+++ b/web/public/mtg/importCards.py
@@ -0,0 +1,92 @@
+import time
+import requests
+import json
+
+# add category name here
+allCategories = ['counterspell', 'beast', 'terror', 'wrath', 'burn']
+
+
+def generate_initial_query(category):
+    string_query = 'https://api.scryfall.com/cards/search?q='
+    if category == 'counterspell':
+        string_query += 'otag%3Acounterspell+t%3Ainstant+not%3Aadventure'
+    elif category == 'beast':
+        string_query += '-type%3Alegendary+type%3Abeast+-type%3Atoken'
+    elif category == 'terror':
+        string_query += 'otag%3Acreature-removal+o%3A%2Fdestroy+target.%2A+%28creature%7Cpermanent%29%2F+%28t' \
+                        '%3Ainstant+or+t%3Asorcery%29+o%3Atarget+not%3Aadventure'
+    elif category == 'wrath':
+        string_query += 'otag%3Asweeper-creature+%28t%3Ainstant+or+t%3Asorcery%29+not%3Aadventure'
+    elif category == 'burn':
+        string_query += '%28c>%3Dr+or+mana>%3Dr%29+%28o%3A%2Fdamage+to+them%2F+or+%28o%3Adeals+o%3Adamage+o%3A' \
+                        '%2Fcontroller%28%5C.%7C+%29%2F%29+or+o%3A%2F~+deals+%28.%7C..%29+damage+to+%28any+target%7C' \
+                        '.*player%28%5C.%7C+or+planeswalker%29%7C.*opponent%28%5C.%7C+or+planeswalker%29%29%2F%29' \
+                        '+%28type%3Ainstant+or+type%3Asorcery%29+not%3Aadventure'
+    # add category string query here
+    string_query += '+-%28set%3Asld+%28%28cn>%3D231+cn<%3D233%29+or+%28cn>%3D321+cn<%3D324%29+or+%28cn>%3D185+cn' \
+                    '<%3D189%29+or+%28cn>%3D138+cn<%3D142%29+or+%28cn>%3D364+cn<%3D368%29+or+cn%3A669+or+cn%3A670%29' \
+                    '%29+-name%3A%2F%5EA-%2F+not%3Adfc+not%3Asplit+-set%3Acmb2+-set%3Acmb1+-set%3Aplist+-set%3Adbl' \
+                    '+-frame%3Aextendedart+language%3Aenglish&unique=art&page='
+    print(string_query)
+    return string_query
+
+
+def fetch_and_write_all(category, query):
+    count = 1
+    will_repeat = True
+    while will_repeat:
+        will_repeat = fetch_and_write(category, query, count)
+        count += 1
+
+
+def fetch_and_write(category, query, count):
+    query += str(count)
+    response = requests.get(f"{query}").json()
+    time.sleep(0.1)
+    with open('jsons/' + category + str(count) + '.json', 'w') as f:
+        json.dump(to_compact_write_form(response), f)
+    return response['has_more']
+
+
+def to_compact_write_form(response):
+    fieldsToUse = ['has_more']
+    fieldsInCard = ['name', 'image_uris', 'content_warning', 'flavor_name', 'reprint', 'frame_effects', 'digital',
+                    'set_type']
+    smallJson = dict()
+    data = []
+    # write all fields needed in response
+    for field in fieldsToUse:
+        smallJson[field] = response[field]
+    # write all fields needed in card
+    for card in response['data']:
+        write_card = dict()
+        for field in fieldsInCard:
+            if field == 'name' and 'card_faces' in card:
+                write_card['name'] = card['card_faces'][0]['name']
+            elif field == 'image_uris':
+                write_card['image_uris'] = write_image_uris(card['image_uris'])
+            elif field in card:
+                write_card[field] = card[field]
+        data.append(write_card)
+    smallJson['data'] = data
+    return smallJson
+
+
+# only write images needed
+def write_image_uris(card_image_uris):
+    image_uris = dict()
+    if 'normal' in card_image_uris:
+        image_uris['normal'] = card_image_uris['normal']
+    elif 'large' in card_image_uris:
+        image_uris['normal'] = card_image_uris['large']
+    elif 'small' in card_image_uris:
+        image_uris['normal'] = card_image_uris['small']
+    if card_image_uris:
+        image_uris['art_crop'] = card_image_uris['art_crop']
+    return image_uris
+
+
+if __name__ == "__main__":
+    for category in allCategories:
+        print(category)
+        fetch_and_write_all(category, generate_initial_query(category))
diff --git a/web/public/mtg/index.html b/web/public/mtg/index.html
new file mode 100644
index 00000000..62849462
--- /dev/null
+++ b/web/public/mtg/index.html
@@ -0,0 +1,202 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <!-- Google Tag Manager -->
+    <script>
+      ;(function (w, d, s, l, i) {
+        w[l] = w[l] || []
+        w[l].push({
+          'gtm.start': new Date().getTime(),
+          event: 'gtm.js',
+        })
+        var f = d.getElementsByTagName(s)[0],
+          j = d.createElement(s),
+          dl = l !== 'dataLayer' ? '&l=' + l : ''
+        j.async = true
+        j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl
+        f.parentNode.insertBefore(j, f)
+      })(window, document, 'script', 'dataLayer', 'GTM-M3MBVGG')
+    </script>
+    <!-- End Google Tag Manager -->
+    <meta charset="UTF-8" />
+    <style type="text/css">
+      body {
+        position: relative;
+      }
+
+      .play-page {
+        display: flex;
+        flex-direction: row-reverse;
+        font-family: Georgia, 'Times New Roman', Times, serif;
+        min-height: 200px;
+      }
+
+      h1,
+      h3 {
+        font-family: Verdana, Geneva, Tahoma, sans-serif;
+        text-align: center;
+      }
+
+      #submit {
+        margin-top: 10px;
+        padding: 8px 20px;
+        background-color: cadetblue;
+        border: none;
+        border-radius: 3px;
+        font-size: 1.1em;
+        color: white;
+        cursor: pointer;
+      }
+
+      #submit:hover {
+        background-color: rgb(0, 146, 156);
+      }
+
+      [type='radio'] {
+        display: none;
+      }
+
+      [type='radio'] + label.radio-label {
+        background: lightgrey;
+        display: block;
+        padding: 10px;
+        border-radius: 4px;
+        cursor: pointer;
+      }
+
+      label.radio-label:hover {
+        background: darkgrey;
+      }
+
+      [type='radio']:checked + label.radio-label {
+        background: lightcoral;
+      }
+
+      .radio-label h3 {
+        margin: 0;
+        display: inline-block;
+        vertical-align: middle;
+        width: 220px;
+      }
+
+      .thumbnail {
+        display: inline-block;
+        vertical-align: middle;
+        width: 67px;
+        height: 48px;
+        margin-right: 4px;
+      }
+
+      body {
+        padding: 70px 0 30px;
+      }
+
+      #addl-options {
+        position: absolute;
+        top: 30px;
+        right: 30px;
+        background-color: white;
+        padding: 10px;
+        cursor: pointer;
+        width: 200px;
+      }
+
+      #addl-options > summary {
+        list-style: none;
+        text-align: right;
+      }
+    </style>
+  </head>
+  <body>
+    <!-- Google Tag Manager (noscript) -->
+    <noscript>
+      <iframe
+        src="https://www.googletagmanager.com/ns.html?id=GTM-M3MBVGG"
+        height="0"
+        width="0"
+        style="display: none; visibility: hidden"
+      ></iframe>
+    </noscript>
+    <!-- End Google Tag Manager (noscript) -->
+    <h1>Magic the Guessering</h1>
+    <div class="play-page" style="justify-content: center">
+      <form
+        method="get"
+        action="guess.html"
+        style="display: flex; flex-direction: column; align-items: center"
+      >
+        <input
+          type="radio"
+          id="counterspell"
+          name="whichguesser"
+          value="counterspell"
+          checked
+        />
+        <label class="radio-label" for="counterspell">
+          <img
+            class="thumbnail"
+            src="https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/1/71cfcba5-1571-48b8-a3db-55dca135506e.jpg?1562843855"
+          />
+          <h3>Counterspell Guesser</h3></label
+        ><br />
+
+        <input type="radio" id="burn" name="whichguesser" value="burn" />
+        <label class="radio-label" for="burn">
+          <img
+            class="thumbnail"
+            src="https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/0/60b2fae1-242b-45e0-a757-b1adc02c06f3.jpg?1562760596"
+          />
+          <h3>Match With Hot Singles</h3></label
+        ><br />
+
+        <details id="addl-options">
+          <summary>
+            <img
+              src="https://mythicspoiler.com/images/buttons/ustset.png"
+              style="width: 32px; vertical-align: top"
+            />
+            Options
+          </summary>
+          <input type="checkbox" name="digital" id="digital" checked />
+          <label for="digital">include digital cards</label>
+          <br />
+          <input type="checkbox" name="un" id="un" checked />
+          <label for="un">include un-cards</label>
+          <br />
+          <input type="checkbox" name="original" id="original" />
+          <label for="original">restrict to only original printing</label>
+        </details>
+        <input type="submit" id="submit" value="Play" />
+      </form>
+    </div>
+
+    <div style="margin: -40px 0 0; height: 60px">
+      <a href="https://paypal.me/idamayer">Donate, buy us a boba 🧋</a>
+    </div>
+
+    <div
+      style="
+        font-size: 0.9em;
+        position: absolute;
+        bottom: 0;
+        left: 0;
+        right: 0;
+        color: grey;
+        font-style: italic;
+      "
+    >
+      made by
+      <a
+        style="color: rgb(0, 146, 156); font-style: italic"
+        href="https://idamayer.com"
+        >Ida Mayer</a
+      >
+      &
+      <a
+        style="color: rgb(0, 146, 156); font-style: italic"
+        href="mailto:alexlien.alien@gmail.com"
+        >Alex Lien</a
+      >, 2022
+    </div>
+  </body>
+</html>
diff --git a/web/public/mtg/jsons/burn1.json b/web/public/mtg/jsons/burn1.json
new file mode 100644
index 00000000..885fcb4e
--- /dev/null
+++ b/web/public/mtg/jsons/burn1.json
@@ -0,0 +1 @@
+{"has_more": true, "data": [{"name": "Angrath's Fury", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/0/708006ba-d494-4093-b108-8249b110831e.jpg?1555041214", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/0/708006ba-d494-4093-b108-8249b110831e.jpg?1555041214"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Annihilating Fire", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/e/ae12fd10-c13e-4777-a233-96204ec75ac1.jpg?1562791532", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/e/ae12fd10-c13e-4777-a233-96204ec75ac1.jpg?1562791532"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Arc Blade", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/d/4d1c04fb-213f-4be1-9bba-94c737826bf8.jpg?1562910601", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/d/4d1c04fb-213f-4be1-9bba-94c737826bf8.jpg?1562910601"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Arc Trail", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/4/445e3a0a-29a7-4dc0-80fe-569b9e751db3.jpg?1562816934", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/4/445e3a0a-29a7-4dc0-80fe-569b9e751db3.jpg?1562816934"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Arrow Storm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/5/c57534fb-2591-4003-aeec-6452faa4a759.jpg?1562793262", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/5/c57534fb-2591-4003-aeec-6452faa4a759.jpg?1562793262"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Artillerize", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/3/034522ae-f531-44d9-b186-ada046ce0abc.jpg?1562875185", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/3/034522ae-f531-44d9-b186-ada046ce0abc.jpg?1562875185"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Atarka's Command", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/0/903d78c9-c5b3-45c3-a6d0-7e92b4196ae3.jpg?1562789860", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/0/903d78c9-c5b3-45c3-a6d0-7e92b4196ae3.jpg?1562789860"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Backlash", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/a/dadf030d-5451-43fc-bf0c-c1629fdf88ec.jpg?1562938984", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/a/dadf030d-5451-43fc-bf0c-c1629fdf88ec.jpg?1562938984"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Banefire", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/1/b188c68a-e9df-4803-a722-1993dd88f833.jpg?1562803150", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/1/b188c68a-e9df-4803-a722-1993dd88f833.jpg?1562803150"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Barbed Lightning", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/5/2509482a-68d8-4e94-9d1e-5b069ebdc2e4.jpg?1562635839", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/5/2509482a-68d8-4e94-9d1e-5b069ebdc2e4.jpg?1562635839"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Beacon of Destruction", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/0/c0fae532-7189-450e-aa7f-e639163278fc.jpg?1562879532", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/0/c0fae532-7189-450e-aa7f-e639163278fc.jpg?1562879532"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Blast from the Past", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/c/5ca23782-80d3-4656-afba-f8440c813253.jpg?1562488402", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/c/5ca23782-80d3-4656-afba-f8440c813253.jpg?1562488402"}, "reprint": false, "frame_effects": ["tombstone"], "digital": false, "set_type": "funny"}, {"name": "Blaze", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/6/26f8c6ab-ae62-4e2e-a5ba-2ec5bbe22445.jpg?1562234516", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/6/26f8c6ab-ae62-4e2e-a5ba-2ec5bbe22445.jpg?1562234516"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Blaze", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/8/a8b6cfd3-4fb1-40a7-a090-de6f8b283cb3.jpg?1562257515", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/8/a8b6cfd3-4fb1-40a7-a090-de6f8b283cb3.jpg?1562257515"}, "reprint": true, "digital": false, "set_type": "starter"}, {"name": "Blaze", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/9/3940d0ca-0ca2-4446-9330-a554c3e89824.jpg?1562908488", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/9/3940d0ca-0ca2-4446-9330-a554c3e89824.jpg?1562908488"}, "reprint": true, "digital": false, "set_type": "starter"}, {"name": "Blaze", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/1/f175c959-3b5d-46a3-9194-fad2359bbff9.jpg?1546740055", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/1/f175c959-3b5d-46a3-9194-fad2359bbff9.jpg?1546740055"}, "reprint": false, "digital": false, "set_type": "starter"}, {"name": "Blazing Salvo", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/7/f7d192ef-a174-4df5-b67f-22918c32cf71.jpg?1562941547", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/7/f7d192ef-a174-4df5-b67f-22918c32cf71.jpg?1562941547"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Blightning", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/8/68ba5a86-ef90-45fd-bc7a-e870e91a207c.jpg?1592714653", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/8/68ba5a86-ef90-45fd-bc7a-e870e91a207c.jpg?1592714653"}, "reprint": true, "digital": false, "set_type": "duel_deck"}, {"name": "Blightning", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/c/3c05e8a2-b7d0-4f24-b2ae-8e4db30e5842.jpg?1562702945", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/c/3c05e8a2-b7d0-4f24-b2ae-8e4db30e5842.jpg?1562702945"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Blightning", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/a/7a1b4c07-588a-444f-9677-3eb1493b5394.jpg?1561757438", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/a/7a1b4c07-588a-444f-9677-3eb1493b5394.jpg?1561757438"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Blur of Blades", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/5/b53f5b9b-d24b-4e9a-bc90-7ed198cd1132.jpg?1562811539", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/5/b53f5b9b-d24b-4e9a-bc90-7ed198cd1132.jpg?1562811539"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Bolt of Keranos", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/d/4df70b14-5d67-4a92-aaba-72480c621d10.jpg?1593092169", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/d/4df70b14-5d67-4a92-aaba-72480c621d10.jpg?1593092169"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Bonfire of the Damned", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/6/e60610fe-891d-46de-b556-d03b637dccec.jpg?1592709031", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/6/e60610fe-891d-46de-b556-d03b637dccec.jpg?1592709031"}, "reprint": false, "frame_effects": ["miracle"], "digital": false, "set_type": "expansion"}, {"name": "Book Burning", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/e/bead678c-7b6a-4668-9919-623312e08a65.jpg?1562631756", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/e/bead678c-7b6a-4668-9919-623312e08a65.jpg?1562631756"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Boros Charm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/4/d4ddf9cc-40a7-4b4f-bb51-b08171453c9a.jpg?1561848093", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/4/d4ddf9cc-40a7-4b4f-bb51-b08171453c9a.jpg?1561848093"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Boros Charm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/c/ac8cd7a1-3f79-405b-8930-2206f32c2035.jpg?1622938151", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/c/ac8cd7a1-3f79-405b-8930-2206f32c2035.jpg?1622938151"}, "reprint": true, "digital": false, "set_type": "box"}, {"name": "Breaking Point", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/6/765ec2c9-8ffe-488a-bebe-e5dd63825a8c.jpg?1562630501", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/6/765ec2c9-8ffe-488a-bebe-e5dd63825a8c.jpg?1562630501"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Breath of Darigaaz", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/8/480bb7e3-df03-454d-ada0-592ef8a4a6f0.jpg?1562909692", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/8/480bb7e3-df03-454d-ada0-592ef8a4a6f0.jpg?1562909692"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Breath of Malfegor", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/a/7a12e4d0-8471-46ac-85e4-a2ea5be8bf8f.jpg?1562642287", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/a/7a12e4d0-8471-46ac-85e4-a2ea5be8bf8f.jpg?1562642287"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Breath of Malfegor", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/b/5b3eb5c5-7ff8-4557-afe7-056ea5f09a49.jpg?1561757216", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/b/5b3eb5c5-7ff8-4557-afe7-056ea5f09a49.jpg?1561757216"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Brimstone Volley", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/9/6960f2da-6b84-4680-8ab2-f0567a5d1b0a.jpg?1562831550", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/9/6960f2da-6b84-4680-8ab2-f0567a5d1b0a.jpg?1562831550"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Browbeat", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/7/77c0eb52-8e09-471a-b00c-aaa1ae244afc.jpg?1592714628", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/7/77c0eb52-8e09-471a-b00c-aaa1ae244afc.jpg?1592714628"}, "reprint": true, "digital": false, "set_type": "duel_deck"}, {"name": "Browbeat", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/4/74f20068-f225-4055-be7a-5c4a18e33b0b.jpg?1562630478", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/4/74f20068-f225-4055-be7a-5c4a18e33b0b.jpg?1562630478"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Browbeat", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/1/1170ee2d-ab25-4c7f-a910-cc01471a2cab.jpg?1562639679", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/1/1170ee2d-ab25-4c7f-a910-cc01471a2cab.jpg?1562639679"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Burn from Within", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/8/f8bdc165-4c6f-47e6-8bda-877c0be3613b.jpg?1576384673", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/8/f8bdc165-4c6f-47e6-8bda-877c0be3613b.jpg?1576384673"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Burning Fields", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/e/dee12f01-581e-4a3c-a8b5-41bef2516781.jpg?1562257986", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/e/dee12f01-581e-4a3c-a8b5-41bef2516781.jpg?1562257986"}, "reprint": false, "digital": false, "set_type": "starter"}, {"name": "Burn the Accursed", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/f/ff4d4e6b-564d-46da-8e32-09ed08c8ddc5.jpg?1634350484", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/f/ff4d4e6b-564d-46da-8e32-09ed08c8ddc5.jpg?1634350484"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Burn the Impure", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/5/b5641730-428d-4484-866e-ec1ac669537f.jpg?1562614054", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/5/b5641730-428d-4484-866e-ec1ac669537f.jpg?1562614054"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Burn Trail", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/f/7f01f9a0-f1d0-4241-a270-df4ed673d1fd.jpg?1562832261", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/f/7f01f9a0-f1d0-4241-a270-df4ed673d1fd.jpg?1562832261"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Burst Lightning", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/d/2dc16614-5cf8-444d-a5ae-cac25018af68.jpg?1562610949", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/d/2dc16614-5cf8-444d-a5ae-cac25018af68.jpg?1562610949"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Burst Lightning", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/b/db539e3e-cefe-4f2c-bc8e-df049426895f.jpg?1561758208", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/b/db539e3e-cefe-4f2c-bc8e-df049426895f.jpg?1561758208"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Cackling Flames", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/5/a54a371e-fb82-41f1-892c-975f932b668e.jpg?1593273099", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/5/a54a371e-fb82-41f1-892c-975f932b668e.jpg?1593273099"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Call In a Professional", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/a/ead68c0a-eed1-4a9c-a790-56f8a79b444c.jpg?1649936108", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/a/ead68c0a-eed1-4a9c-a790-56f8a79b444c.jpg?1649936108"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Carbonize", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/4/d4b4767b-edd1-4e36-b363-52114a9afe5e.jpg?1580014480", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/4/d4b4767b-edd1-4e36-b363-52114a9afe5e.jpg?1580014480"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Carbonize", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/f/6f565fa1-a1a0-4dd0-b7f4-df65a807d156.jpg?1562530228", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/f/6f565fa1-a1a0-4dd0-b7f4-df65a807d156.jpg?1562530228"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Cave-In", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/4/440d9d26-f304-467d-af79-914cc65f082e.jpg?1562380418", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/4/440d9d26-f304-467d-af79-914cc65f082e.jpg?1562380418"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Chain Lightning", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/5/b5883762-ca0a-4932-8d2a-41a45796a5f8.jpg?1562860651", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/5/b5883762-ca0a-4932-8d2a-41a45796a5f8.jpg?1562860651"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Chain Lightning", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/f/bfb7fe8e-e348-4bf9-aa71-65f0675147e4.jpg?1636769610", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/f/bfb7fe8e-e348-4bf9-aa71-65f0675147e4.jpg?1636769610"}, "reprint": true, "digital": false, "set_type": "box"}, {"name": "Chain Lightning", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/4/14bd3d19-033e-41a7-8710-02b73ba0b4e4.jpg?1562899148", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/4/14bd3d19-033e-41a7-8710-02b73ba0b4e4.jpg?1562899148"}, "reprint": true, "digital": false, "set_type": "masterpiece"}, {"name": "Chain Lightning", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/c/9ca05db2-ad92-4f4a-992d-b7f08f4f9c26.jpg?1562928035", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/c/9ca05db2-ad92-4f4a-992d-b7f08f4f9c26.jpg?1562928035"}, "reprint": true, "digital": false, "set_type": "premium_deck"}, {"name": "Chain of Plasma", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/9/f94aa774-9036-4016-8880-4bde2710cb90.jpg?1562954081", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/9/f94aa774-9036-4016-8880-4bde2710cb90.jpg?1562954081"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Chandra's Fury", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/7/e761acf6-6618-44cc-8f65-1d7ad7e520fe.jpg?1561758344", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/7/e761acf6-6618-44cc-8f65-1d7ad7e520fe.jpg?1561758344"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Chandra's Outburst", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/1/f1e849c3-f357-4e81-a580-be5056bed51b.jpg?1562745440", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/1/f1e849c3-f357-4e81-a580-be5056bed51b.jpg?1562745440"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Chandra's Outrage", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/2/3282db18-8564-418e-8c26-62e610b160f2.jpg?1562905547", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/2/3282db18-8564-418e-8c26-62e610b160f2.jpg?1562905547"}, "reprint": false, "digital": false, "set_type": "archenemy"}, {"name": "Char", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/f/ff3a24af-e995-4d05-ac2c-e9676048675d.jpg?1598915384", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/f/ff3a24af-e995-4d05-ac2c-e9676048675d.jpg?1598915384"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Char", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/d/3dc5f957-c1e4-452d-a78b-8d772ea0b940.jpg?1561756964", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/d/3dc5f957-c1e4-452d-a78b-8d772ea0b940.jpg?1561756964"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Cinder Cloud", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/0/f044c470-50ce-4a6c-b8ab-665357c3c11e.jpg?1562722408", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/0/f044c470-50ce-4a6c-b8ab-665357c3c11e.jpg?1562722408"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Cinder Storm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/e/9e2d16c1-6226-438f-be1e-eaab3df687e1.jpg?1562875024", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/e/9e2d16c1-6226-438f-be1e-eaab3df687e1.jpg?1562875024"}, "reprint": false, "digital": false, "set_type": "starter"}, {"name": "Clan Defiance", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/f/efa05298-9c94-4179-b75a-49ee2ca92920.jpg?1561851654", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/f/efa05298-9c94-4179-b75a-49ee2ca92920.jpg?1561851654"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Cleansing Screech", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/9/79928b26-fcac-4c3f-9edd-292769c2e56e.jpg?1562131561", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/9/79928b26-fcac-4c3f-9edd-292769c2e56e.jpg?1562131561"}, "reprint": false, "digital": false, "set_type": "duel_deck"}, {"name": "Collateral Damage", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/b/fb738362-b0b4-4811-9fbf-5f45c852c822.jpg?1562831834", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/b/fb738362-b0b4-4811-9fbf-5f45c852c822.jpg?1562831834"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Collective Defiance", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/9/8960883f-3813-412b-9a5b-f8cf8d566fac.jpg?1576384546", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/9/8960883f-3813-412b-9a5b-f8cf8d566fac.jpg?1576384546"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Concussive Bolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/1/41b68e85-a381-441d-aa18-491f9e202a10.jpg?1562610848", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/1/41b68e85-a381-441d-aa18-491f9e202a10.jpg?1562610848"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Cone of Flame", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/e/bec5e56a-5bab-4965-9035-128c3f1ae175.jpg?1562554444", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/e/bec5e56a-5bab-4965-9035-128c3f1ae175.jpg?1562554444"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Cone of Flame", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/7/5713f17a-9a57-41f8-b492-ced876e1a37f.jpg?1562800924", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/7/5713f17a-9a57-41f8-b492-ced876e1a37f.jpg?1562800924"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Consuming Sinkhole", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/2/82a42b28-3d1b-4432-b8c9-2d42e4d0e1c5.jpg?1562921426", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/2/82a42b28-3d1b-4432-b8c9-2d42e4d0e1c5.jpg?1562921426"}, "reprint": false, "frame_effects": ["devoid"], "digital": false, "set_type": "expansion"}, {"name": "Crackling Doom", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/8/f83c7d53-2599-42a9-ae96-a2699c5164cb.jpg?1562796251", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/8/f83c7d53-2599-42a9-ae96-a2699c5164cb.jpg?1562796251"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Crater's Claws", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/5/95dde66b-b4a1-4a1e-8c9e-0bec4790b1e5.jpg?1562790652", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/5/95dde66b-b4a1-4a1e-8c9e-0bec4790b1e5.jpg?1562790652"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Creative Outburst", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/a/eab58d87-bf01-45dc-8958-e2b3375f914b.jpg?1627428357", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/a/eab58d87-bf01-45dc-8958-e2b3375f914b.jpg?1627428357"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Cryoclasm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/a/6a892711-a1a4-4402-957f-92077d00320d.jpg?1593275219", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/a/6a892711-a1a4-4402-957f-92077d00320d.jpg?1593275219"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Culmination of Studies", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/4/2483060e-9d3f-48ae-80ea-0119bf6b4d67.jpg?1627428427", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/4/2483060e-9d3f-48ae-80ea-0119bf6b4d67.jpg?1627428427"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Cunning Strike", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/4/e4991f81-3190-4d33-bf09-9d5387cbec11.jpg?1562830894", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/4/e4991f81-3190-4d33-bf09-9d5387cbec11.jpg?1562830894"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Darigaaz's Charm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/f/cf4c9d6a-86eb-45be-9405-473eb263b94c.jpg?1562938851", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/f/cf4c9d6a-86eb-45be-9405-473eb263b94c.jpg?1562938851"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Deal Damage", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/e/de905517-983d-4996-a680-3a5cf91bfe11.jpg?1562489827", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/e/de905517-983d-4996-a680-3a5cf91bfe11.jpg?1562489827"}, "reprint": false, "digital": false, "set_type": "funny"}, {"name": "Death Spark", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/a/ba841b44-475c-402c-ac11-763de0cf27d9.jpg?1562770162", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/a/ba841b44-475c-402c-ac11-763de0cf27d9.jpg?1562770162"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Deflecting Palm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/2/32374918-1bcb-4516-96af-f27da752517e.jpg?1562784565", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/2/32374918-1bcb-4516-96af-f27da752517e.jpg?1562784565"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Demonfire", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/f/af2ad333-722e-4d7e-972a-903c24068931.jpg?1593273111", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/f/af2ad333-722e-4d7e-972a-903c24068931.jpg?1593273111"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Destructive Revelry", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/c/bc2eb53a-3d0f-4bb3-be36-f8024f2a1d4d.jpg?1592752246", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/c/bc2eb53a-3d0f-4bb3-be36-f8024f2a1d4d.jpg?1592752246"}, "reprint": true, "digital": false, "set_type": "duel_deck"}, {"name": "Detonate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/3/237eedf5-8a8f-4668-a911-e2bf66f8221e.jpg?1562138293", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/3/237eedf5-8a8f-4668-a911-e2bf66f8221e.jpg?1562138293"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Detonate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/f/ffd7eb90-ae95-49df-898a-9510187bce1c.jpg?1562949167", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/f/ffd7eb90-ae95-49df-898a-9510187bce1c.jpg?1562949167"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Devastate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/f/bfe7c990-a34b-475e-a612-447c22f998d3.jpg?1562930849", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/f/bfe7c990-a34b-475e-a612-447c22f998d3.jpg?1562930849"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Devil's Play", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/8/c80596a4-b464-4b9e-8186-94a1c44838eb.jpg?1562836883", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/8/c80596a4-b464-4b9e-8186-94a1c44838eb.jpg?1562836883"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Devil's Play", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/6/e6dd2f9e-16c2-4d25-98c4-0017ccd42228.jpg?1561758340", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/6/e6dd2f9e-16c2-4d25-98c4-0017ccd42228.jpg?1561758340"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Direct Current", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/6/166b0d75-824c-4c04-833b-7f7c69569a18.jpg?1572893128", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/6/166b0d75-824c-4c04-833b-7f7c69569a18.jpg?1572893128"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Disintegrate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/7/8712c49e-f171-4669-bed9-87575a37af11.jpg?1559591574", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/7/8712c49e-f171-4669-bed9-87575a37af11.jpg?1559591574"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Disintegrate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/3/93ca09e6-2f23-4457-80ab-c7806112888b.jpg?1562546639", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/3/93ca09e6-2f23-4457-80ab-c7806112888b.jpg?1562546639"}, "reprint": true, "digital": true, "set_type": "promo"}, {"name": "Double Deal", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/d/ed8b3def-30ee-4dd2-9a25-ecf7d5663f96.jpg?1562799187", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/d/ed8b3def-30ee-4dd2-9a25-ecf7d5663f96.jpg?1562799187"}, "reprint": false, "digital": false, "set_type": "funny"}, {"name": "Draconic Roar", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/c/6cf5591c-46e3-4904-8b4e-4f1f84d3118f.jpg?1562787954", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/c/6cf5591c-46e3-4904-8b4e-4f1f84d3118f.jpg?1562787954"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Dragon's Approach", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/c/0cb504a0-1dfb-49d0-84c3-7bd318d55481.jpg?1624591696", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/c/0cb504a0-1dfb-49d0-84c3-7bd318d55481.jpg?1624591696"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Earthquake", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/f/8f04dc5c-2764-42d0-974e-6d902222c138.jpg?1562242701", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/f/8f04dc5c-2764-42d0-974e-6d902222c138.jpg?1562242701"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Earthquake", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/5/05126438-e806-43e6-bd81-233b629b4a1b.jpg?1562896224", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/5/05126438-e806-43e6-bd81-233b629b4a1b.jpg?1562896224"}, "reprint": true, "digital": false, "set_type": "starter"}, {"name": "Earthquake", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/7/272f65a3-3c0c-417d-b5b6-276a643d643e.jpg?1562446144", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/7/272f65a3-3c0c-417d-b5b6-276a643d643e.jpg?1562446144"}, "reprint": true, "digital": false, "set_type": "starter"}, {"name": "Earthquake", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/1/01bde909-899d-4efc-aac5-57b69fa764db.jpg?1562588740", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/1/01bde909-899d-4efc-aac5-57b69fa764db.jpg?1562588740"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Earthquake", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/6/e68ac362-6cdc-48a6-bdd3-4f8ea32add64.jpg?1559591701", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/6/e68ac362-6cdc-48a6-bdd3-4f8ea32add64.jpg?1559591701"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Electrodominance", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/c/5c63877b-cdab-4ce4-a1c0-c088eb62a57a.jpg?1584830858", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/c/5c63877b-cdab-4ce4-a1c0-c088eb62a57a.jpg?1584830858"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Electrostatic Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/6/36ba1ac9-ebb9-449d-bd3b-716631b112fb.jpg?1645416206", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/6/36ba1ac9-ebb9-449d-bd3b-716631b112fb.jpg?1645416206"}, "reprint": false, "digital": true, "set_type": "alchemy"}, {"name": "Ember Shot", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/a/6a9eb72b-9ae2-4b64-bbb9-187446b5fd2f.jpg?1562630295", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/a/6a9eb72b-9ae2-4b64-bbb9-187446b5fd2f.jpg?1562630295"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Energy Bolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/1/711f4cff-0256-44b2-a2fe-1cae6e9edb2b.jpg?1562719783", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/1/711f4cff-0256-44b2-a2fe-1cae6e9edb2b.jpg?1562719783"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Essence Backlash", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/9/a98609dc-ea90-4c7e-a191-5e5d0ba16847.jpg?1562791298", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/9/a98609dc-ea90-4c7e-a191-5e5d0ba16847.jpg?1562791298"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Eternal Flame", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/6/d646feea-3c20-4737-8d20-ffad42258ced.jpg?1562946085", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/6/d646feea-3c20-4737-8d20-ffad42258ced.jpg?1562946085"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Exploding Borders", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/2/f247aaaf-4d65-4dfc-bab2-3c1331762647.jpg?1562804623", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/2/f247aaaf-4d65-4dfc-bab2-3c1331762647.jpg?1562804623"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Explosive Impact", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/a/3a3e2b45-b086-4ffd-aa1a-1d03046e0d61.jpg?1562785002", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/a/3a3e2b45-b086-4ffd-aa1a-1d03046e0d61.jpg?1562785002"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Explosive Singularity", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/6/e6cdd822-44a1-4d58-9de4-69fc56eae255.jpg?1654567601", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/6/e6cdd822-44a1-4d58-9de4-69fc56eae255.jpg?1654567601"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Explosive Singularity", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/1/a1d47e98-daae-42f7-9581-1269d57bd16e.jpg?1654570003", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/1/a1d47e98-daae-42f7-9581-1269d57bd16e.jpg?1654570003"}, "reprint": false, "frame_effects": ["showcase"], "digital": false, "set_type": "expansion"}, {"name": "Explosive Welcome", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/2/122c01e6-38a6-456e-971e-9004df85ac1c.jpg?1624591777", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/2/122c01e6-38a6-456e-971e-9004df85ac1c.jpg?1624591777"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Exquisite Firecraft", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/2/42eca98e-a164-4f70-a0b0-7a604863f30b.jpg?1562016890", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/2/42eca98e-a164-4f70-a0b0-7a604863f30b.jpg?1562016890"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Exquisite Firecraft", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/b/0be814f7-3c35-4b82-9fda-b8750a77cb9b.jpg?1562542837", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/b/0be814f7-3c35-4b82-9fda-b8750a77cb9b.jpg?1562542837"}, "reprint": true, "digital": true, "set_type": "promo"}, {"name": "Face to Face", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/4/64c93900-1af7-4c6b-a844-055bb7e27ddb.jpg?1562488406", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/4/64c93900-1af7-4c6b-a844-055bb7e27ddb.jpg?1562488406"}, "reprint": false, "digital": false, "set_type": "funny"}, {"name": "Fanning the Flames", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/9/79075361-e6ee-4cc9-990b-88fef27bbb1c.jpg?1562596865", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/9/79075361-e6ee-4cc9-990b-88fef27bbb1c.jpg?1562596865"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Farideh's Fireball", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/7/57a46987-f05a-4b83-af56-f18000874e65.jpg?1627706196", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/7/57a46987-f05a-4b83-af56-f18000874e65.jpg?1627706196"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Fateful End", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/6/56455067-92c0-45b5-ac2e-525c35b41215.jpg?1581480134", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/6/56455067-92c0-45b5-ac2e-525c35b41215.jpg?1581480134"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Fault Line", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/a/cab4fd0e-9f84-4628-92a7-858ad8064531.jpg?1562937807", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/a/cab4fd0e-9f84-4628-92a7-858ad8064531.jpg?1562937807"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Fiery Confluence", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/b/7b61c9bc-16e8-417f-99e7-8bd83d4666c5.jpg?1562706203", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/b/7b61c9bc-16e8-417f-99e7-8bd83d4666c5.jpg?1562706203"}, "reprint": false, "digital": false, "set_type": "commander"}, {"name": "Fiery Confluence", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/c/4c454a20-8ec8-41d9-b9c3-acaa510d050b.jpg?1593559583", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/c/4c454a20-8ec8-41d9-b9c3-acaa510d050b.jpg?1593559583"}, "reprint": true, "digital": false, "set_type": "spellbook"}, {"name": "Fiery Gambit", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/9/a91376ed-5868-4887-8389-5ef5b9471786.jpg?1562153660", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/9/a91376ed-5868-4887-8389-5ef5b9471786.jpg?1562153660"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Fiery Temper", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/1/61caf82d-e077-4931-a6ad-09fa7f04b36f.jpg?1576384730", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/1/61caf82d-e077-4931-a6ad-09fa7f04b36f.jpg?1576384730"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Fiery Temper", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/1/918e46b7-cbca-4acf-8e83-94b5fcadcc49.jpg?1562630935", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/1/918e46b7-cbca-4acf-8e83-94b5fcadcc49.jpg?1562630935"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Fiery Temper", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/3/73493d43-7952-4202-818d-a1a05788af6f.jpg?1562636814", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/3/73493d43-7952-4202-818d-a1a05788af6f.jpg?1562636814"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Fiery Temper", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/3/d377a7b9-5c25-4017-84a8-ae368eceba50.jpg?1561758137", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/3/d377a7b9-5c25-4017-84a8-ae368eceba50.jpg?1561758137"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Fire Ambush", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/d/4dd8bdbd-99c9-4fa7-936a-acc7f4238507.jpg?1562256089", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/d/4dd8bdbd-99c9-4fa7-936a-acc7f4238507.jpg?1562256089"}, "reprint": false, "digital": false, "set_type": "starter"}, {"name": "Fireblast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/4/44ab6601-409b-416f-a26c-b995e08fe6f3.jpg?1562908902", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/4/44ab6601-409b-416f-a26c-b995e08fe6f3.jpg?1562908902"}, "reprint": true, "digital": true, "set_type": "masters"}, {"name": "Fireblast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/1/b1eb5b2c-1f02-48a6-a287-88eb189d6780.jpg?1562278616", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/1/b1eb5b2c-1f02-48a6-a287-88eb189d6780.jpg?1562278616"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Firebolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/0/90aae741-88af-4d21-a230-9a2592acdc87.jpg?1580014536", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/0/90aae741-88af-4d21-a230-9a2592acdc87.jpg?1580014536"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Firebolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/5/d5e45005-dd81-4d80-b043-02f719aca929.jpg?1562934963", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/5/d5e45005-dd81-4d80-b043-02f719aca929.jpg?1562934963"}, "reprint": false, "frame_effects": ["tombstone"], "digital": false, "set_type": "expansion"}, {"name": "Fires of Undeath", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/d/6d94aaa4-c2fd-4714-9198-8415158b9c4d.jpg?1562920799", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/d/6d94aaa4-c2fd-4714-9198-8415158b9c4d.jpg?1562920799"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Fire Tempest", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/2/92334ebe-3d7a-46de-8b91-931e5d56a5a5.jpg?1562447336", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/2/92334ebe-3d7a-46de-8b91-931e5d56a5a5.jpg?1562447336"}, "reprint": false, "digital": false, "set_type": "starter"}, {"name": "First Volley", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/6/d6e5e360-ed47-40c1-8ad7-57645c2854ca.jpg?1562880074", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/6/d6e5e360-ed47-40c1-8ad7-57645c2854ca.jpg?1562880074"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Flamebreak", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/7/87e1f06f-7c87-4da8-b339-e571e391cab1.jpg?1562637920", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/7/87e1f06f-7c87-4da8-b339-e571e391cab1.jpg?1562637920"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Flame Burst", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/4/64bbd438-7df2-4d7b-88ad-4531ebaf3931.jpg?1562913643", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/4/64bbd438-7df2-4d7b-88ad-4531ebaf3931.jpg?1562913643"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Flame Jab", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/6/06c2b6b2-485e-41e6-b106-4f6f402e0ec3.jpg?1562896430", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/6/06c2b6b2-485e-41e6-b106-4f6f402e0ec3.jpg?1562896430"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Flame Javelin", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/5/a567b570-81e4-4068-929c-9ce406fe7474.jpg?1562834196", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/5/a567b570-81e4-4068-929c-9ce406fe7474.jpg?1562834196"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Flame Javelin", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/0/407858c8-316d-47a7-8234-c490a0bc87a6.jpg?1561756991", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/0/407858c8-316d-47a7-8234-c490a0bc87a6.jpg?1561756991"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Flame Jet", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/5/a511f9df-b53b-4fea-87cd-9f18f6833f92.jpg?1562444727", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/5/a511f9df-b53b-4fea-87cd-9f18f6833f92.jpg?1562444727"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Flame Lash", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/c/ac44e3cb-cc69-4222-87bc-ffa54b7ab34a.jpg?1562741297", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/c/ac44e3cb-cc69-4222-87bc-ffa54b7ab34a.jpg?1562741297"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Flame Rift", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/6/e63ed449-d249-4639-85d2-f8fe75496d5c.jpg?1626100460", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/6/e63ed449-d249-4639-85d2-f8fe75496d5c.jpg?1626100460"}, "reprint": true, "digital": false, "set_type": "draft_innovation"}, {"name": "Flame Rift", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/7/7717eeb9-c457-4a65-93a0-e91c7f6a1970.jpg?1562630580", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/7/7717eeb9-c457-4a65-93a0-e91c7f6a1970.jpg?1562630580"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Flames of the Blood Hand", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/7/a79701b4-d220-4c3e-b96c-7a77a22ba899.jpg?1651124386", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/7/a79701b4-d220-4c3e-b96c-7a77a22ba899.jpg?1651124386"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Flame Spill", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/3/b3090004-d7dd-47bc-92e5-977be4fd9ae5.jpg?1591227197", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/3/b3090004-d7dd-47bc-92e5-977be4fd9ae5.jpg?1591227197"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Flame Wave", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/0/e069d90a-e7d9-4967-a872-0dd8a0a9934a.jpg?1562597824", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/0/e069d90a-e7d9-4967-a872-0dd8a0a9934a.jpg?1562597824"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Flaming Gambit", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/b/fb7fd9b7-c394-4ab3-b945-b4aab694eb6a.jpg?1562632851", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/b/fb7fd9b7-c394-4ab3-b945-b4aab694eb6a.jpg?1562632851"}, "reprint": false, "frame_effects": ["tombstone"], "digital": false, "set_type": "expansion"}, {"name": "Flare", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/b/abc046c2-be9b-4f93-ac7d-e7dea6c4df9a.jpg?1562593271", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/b/abc046c2-be9b-4f93-ac7d-e7dea6c4df9a.jpg?1562593271"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Flare", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/b/1bd7755f-7ca5-4948-8baf-976823906891.jpg?1617148194", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/b/1bd7755f-7ca5-4948-8baf-976823906891.jpg?1617148194"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Flare", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/5/d5350236-7bd2-462d-9768-50087626c764.jpg?1562934818", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/5/d5350236-7bd2-462d-9768-50087626c764.jpg?1562934818"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Foundry Helix", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/c/9c54b7c6-f94c-4349-8725-319c54240409.jpg?1626098329", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/c/9c54b7c6-f94c-4349-8725-319c54240409.jpg?1626098329"}, "reprint": false, "digital": false, "set_type": "draft_innovation"}, {"name": "Friendly Fire", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/c/ac4272ca-bb15-415c-a589-a472953a0dd9.jpg?1562828722", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/c/ac4272ca-bb15-415c-a589-a472953a0dd9.jpg?1562828722"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Galvanic Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/5/f5881bbc-8600-464d-9dcd-5a7780918d1d.jpg?1562825173", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/5/f5881bbc-8600-464d-9dcd-5a7780918d1d.jpg?1562825173"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Geistblast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/9/19b1dfd9-b717-4c23-b8e5-a6ec835b278a.jpg?1576384765", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/9/19b1dfd9-b717-4c23-b8e5-a6ec835b278a.jpg?1576384765"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Geistflame", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/b/1b856f31-ac80-4338-95a5-3f8acda74cfe.jpg?1562826976", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/b/1b856f31-ac80-4338-95a5-3f8acda74cfe.jpg?1562826976"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Ghitu Fire", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/8/78827acd-a526-411b-bd22-ab9b538c75dd.jpg?1562919168", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/8/78827acd-a526-411b-bd22-ab9b538c75dd.jpg?1562919168"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Ghostfire", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/6/a60475e5-0d37-4af0-b717-da4c8dea45ac.jpg?1562928542", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/6/a60475e5-0d37-4af0-b717-da4c8dea45ac.jpg?1562928542"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Giant's Ire", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/4/046fa2db-4c73-401a-b9a4-b039554be625.jpg?1562336735", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/4/046fa2db-4c73-401a-b9a4-b039554be625.jpg?1562336735"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Glacial Ray", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/4/04c713fd-df47-4b35-bd37-ab65d853bdc8.jpg?1562271537", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/4/04c713fd-df47-4b35-bd37-ab65d853bdc8.jpg?1562271537"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Goblin Barrage", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/8/4849db5d-cd41-49f6-acd5-697cdc8263f6.jpg?1562735067", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/8/4849db5d-cd41-49f6-acd5-697cdc8263f6.jpg?1562735067"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Goblin Grenade", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/9/394cc2aa-0318-4ccd-a550-99a7eac933c3.jpg?1562639104", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/9/394cc2aa-0318-4ccd-a550-99a7eac933c3.jpg?1562639104"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Goblin Grenade", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/8/8837eaba-9602-4f63-9897-85583fcdcf51.jpg?1562920228", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/8/8837eaba-9602-4f63-9897-85583fcdcf51.jpg?1562920228"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Goblin Grenade", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/e/dee262da-3002-4c08-8043-4e40e1b46822.jpg?1562936623", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/e/dee262da-3002-4c08-8043-4e40e1b46822.jpg?1562936623"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Goblin Grenade", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/b/1befdfc7-a1e3-4a2a-ad68-7d0fee170f3f.jpg?1562900237", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/b/1befdfc7-a1e3-4a2a-ad68-7d0fee170f3f.jpg?1562900237"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Grapeshot", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/c/8cd49f85-7dbd-4cb6-b916-2adee29bb745.jpg?1561967853", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/c/8cd49f85-7dbd-4cb6-b916-2adee29bb745.jpg?1561967853"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Grapeshot", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/e/4ee33cb6-768e-44a0-b6f4-b8638aa84330.jpg?1562911525", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/e/4ee33cb6-768e-44a0-b6f4-b8638aa84330.jpg?1562911525"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Grapeshot", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/9/b99b45df-9602-4037-a695-09decb5f21d7.jpg?1623890288", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/9/b99b45df-9602-4037-a695-09decb5f21d7.jpg?1623890288"}, "reprint": true, "digital": false, "set_type": "masterpiece"}, {"name": "Guerrilla Tactics", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/b/9bf3bac0-6e63-4bd3-bbd6-547f46c2d126.jpg?1562552299", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/b/9bf3bac0-6e63-4bd3-bbd6-547f46c2d126.jpg?1562552299"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Guerrilla Tactics", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/3/63535f0e-dc14-420e-bcb7-b5ef8fafb93f.jpg?1562915110", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/3/63535f0e-dc14-420e-bcb7-b5ef8fafb93f.jpg?1562915110"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Guerrilla Tactics", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/1/51811f2a-7002-4ba7-98d8-5b09d887975c.jpg?1562768705", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/1/51811f2a-7002-4ba7-98d8-5b09d887975c.jpg?1562768705"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Guerrilla Tactics", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/c/3c005ca3-0508-4ac2-afec-3d4a27334c31.jpg?1562768254", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/c/3c005ca3-0508-4ac2-afec-3d4a27334c31.jpg?1562768254"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Gut Shot", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/5/a54a2a30-b96a-49c7-9151-1f4b0d4a4413.jpg?1562880417", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/5/a54a2a30-b96a-49c7-9151-1f4b0d4a4413.jpg?1562880417"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Hammer of Bogardan", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/7/f7285f52-5df0-4f90-9cf7-a57295d90fd4.jpg?1562722857", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/7/f7285f52-5df0-4f90-9cf7-a57295d90fd4.jpg?1562722857"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Hanabi Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/8/881fecf4-8c14-4614-84bd-c1a3dcdbb5ff.jpg?1562762458", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/8/881fecf4-8c14-4614-84bd-c1a3dcdbb5ff.jpg?1562762458"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Heartfire", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/d/7db219ea-2ed1-4a86-955c-d61ecedbc019.jpg?1557576716", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/d/7db219ea-2ed1-4a86-955c-d61ecedbc019.jpg?1557576716"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Hidetsugu's Second Rite", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/e/2e48eb77-3bd7-444a-9262-799cc706c05a.jpg?1562493025", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/e/2e48eb77-3bd7-444a-9262-799cc706c05a.jpg?1562493025"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Hungry Flames", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/c/4ca23676-f36f-4266-ba4f-5e9ebf3adb57.jpg?1592419490", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/c/4ca23676-f36f-4266-ba4f-5e9ebf3adb57.jpg?1592419490"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Igneous Inspiration", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/7/5781ad7b-dc1b-4cc1-9e72-6e714b9ba1de.jpg?1624591976", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/7/5781ad7b-dc1b-4cc1-9e72-6e714b9ba1de.jpg?1624591976"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Illuminate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/e/ceef2761-7301-42de-8f54-49b8cd1e457b.jpg?1562943833", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/e/ceef2761-7301-42de-8f54-49b8cd1e457b.jpg?1562943833"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Improvised Weaponry", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/9/29d5fd00-c616-4079-a91e-4da0bcaf9120.jpg?1627706453", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/9/29d5fd00-c616-4079-a91e-4da0bcaf9120.jpg?1627706453"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Incendiary Command", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/1/512367a2-f8f6-4c28-9eb3-8e04d2694e4b.jpg?1562348065", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/1/512367a2-f8f6-4c28-9eb3-8e04d2694e4b.jpg?1562348065"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Incendiary Flow", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/f/cf464f61-8a7f-493b-a80f-2f2b0ebd8bf6.jpg?1576384613", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/f/cf464f61-8a7f-493b-a80f-2f2b0ebd8bf6.jpg?1576384613"}, "reprint": false, "digital": false, "set_type": "expansion"}]}
\ No newline at end of file
diff --git a/web/public/mtg/jsons/burn2.json b/web/public/mtg/jsons/burn2.json
new file mode 100644
index 00000000..c1116e11
--- /dev/null
+++ b/web/public/mtg/jsons/burn2.json
@@ -0,0 +1 @@
+{"has_more": true, "data": [{"name": "Incendiary Flow", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/e/ae93a313-c265-435f-b745-7b7a7ed6208e.jpg?1562636873", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/e/ae93a313-c265-435f-b745-7b7a7ed6208e.jpg?1562636873"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Incinerate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/5/8503210d-be78-4271-a050-53caa94f735d.jpg?1562844302", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/5/8503210d-be78-4271-a050-53caa94f735d.jpg?1562844302"}, "reprint": true, "digital": false, "set_type": "duel_deck"}, {"name": "Incinerate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/2/723fb62e-735a-4ca6-9d38-f1c3944fe69a.jpg?1562549678", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/2/723fb62e-735a-4ca6-9d38-f1c3944fe69a.jpg?1562549678"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Incinerate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/a/aa0f7e1f-bcb5-414f-a2e9-6a158fec2ff5.jpg?1562593262", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/a/aa0f7e1f-bcb5-414f-a2e9-6a158fec2ff5.jpg?1562593262"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Incinerate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/0/409b2be8-5bb6-45e0-ab87-ca73b4e3a396.jpg?1562718795", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/0/409b2be8-5bb6-45e0-ab87-ca73b4e3a396.jpg?1562718795"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Incinerate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/c/9c3f00af-010d-4485-b8b7-47400d99c496.jpg?1562924091", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/c/9c3f00af-010d-4485-b8b7-47400d99c496.jpg?1562924091"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Incinerate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/8/28b0495d-0c3f-4491-8331-4cbabbd6eac5.jpg?1561756819", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/8/28b0495d-0c3f-4491-8331-4cbabbd6eac5.jpg?1561756819"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Inescapable Blaze", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/6/46651efd-0906-4350-a1b8-52e3f8aff45d.jpg?1572893201", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/6/46651efd-0906-4350-a1b8-52e3f8aff45d.jpg?1572893201"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Inferno", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/4/e411b7b5-ab91-410a-af6d-b3a21a8e3b70.jpg?1562249896", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/4/e411b7b5-ab91-410a-af6d-b3a21a8e3b70.jpg?1562249896"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Inferno", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/8/68d04a75-647f-400f-b0dc-c4544f7db2d4.jpg?1562591355", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/8/68d04a75-647f-400f-b0dc-c4544f7db2d4.jpg?1562591355"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Inferno", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/6/a6b61512-5b24-424c-966f-36b595781e14.jpg?1562934483", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/6/a6b61512-5b24-424c-966f-36b595781e14.jpg?1562934483"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Inferno", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/a/3ac1649a-629b-4598-be09-74a57905753f.jpg?1562544107", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/a/3ac1649a-629b-4598-be09-74a57905753f.jpg?1562544107"}, "reprint": true, "digital": true, "set_type": "promo"}, {"name": "Inferno Jet", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/c/0c6a43fe-369d-4943-a825-570eb3cceba4.jpg?1562788752", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/c/0c6a43fe-369d-4943-a825-570eb3cceba4.jpg?1562788752"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Inspired Ultimatum", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/d/dd64f064-8f05-41ef-b95b-1b723137f846.jpg?1591228071", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/d/dd64f064-8f05-41ef-b95b-1b723137f846.jpg?1591228071"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Invoke the Firemind", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/8/58d8e41a-5990-4ceb-9d41-76632faa7883.jpg?1593272700", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/8/58d8e41a-5990-4ceb-9d41-76632faa7883.jpg?1593272700"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Ionize", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/1/f161f7d2-eaa1-4931-93f9-befa8b5df821.jpg?1572893679", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/1/f161f7d2-eaa1-4931-93f9-befa8b5df821.jpg?1572893679"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Izzet Charm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/1/61289196-a56b-4d24-b340-9cf067c77f45.jpg?1592713417", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/1/61289196-a56b-4d24-b340-9cf067c77f45.jpg?1592713417"}, "reprint": true, "digital": false, "set_type": "duel_deck"}, {"name": "Izzet Charm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/8/e8e84a97-8e40-42fa-a114-df90e820ede6.jpg?1562497263", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/8/e8e84a97-8e40-42fa-a114-df90e820ede6.jpg?1562497263"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Jeskai Charm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/a/ca268705-ef04-4bf1-8a5d-866bb3e5bb61.jpg?1562793488", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/a/ca268705-ef04-4bf1-8a5d-866bb3e5bb61.jpg?1562793488"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Kaervek's Purge", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/a/7a42ef95-92ec-40fe-ab30-a476f012a525.jpg?1562720237", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/a/7a42ef95-92ec-40fe-ab30-a476f012a525.jpg?1562720237"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Kaervek's Torch", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/a/0a1624ab-e50e-48a3-acf7-457069914616.jpg?1562717831", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/a/0a1624ab-e50e-48a3-acf7-457069914616.jpg?1562717831"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Kaleidoscorch", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/5/a5f07603-fd79-437a-9b12-495fc5a39b68.jpg?1626096801", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/5/a5f07603-fd79-437a-9b12-495fc5a39b68.jpg?1626096801"}, "reprint": false, "digital": false, "set_type": "draft_innovation"}, {"name": "Kamahl's Sledge", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/8/38c55518-7bdf-4a42-ae30-cd6525557a59.jpg?1562629270", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/8/38c55518-7bdf-4a42-ae30-cd6525557a59.jpg?1562629270"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Kami's Flare", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/e/bef5d58e-b490-4682-9a44-12cd61a94c0f.jpg?1654567705", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/e/bef5d58e-b490-4682-9a44-12cd61a94c0f.jpg?1654567705"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Kindle", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/3/930745eb-b038-4b55-97f3-bf8d99b54d32.jpg?1562055431", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/3/930745eb-b038-4b55-97f3-bf8d99b54d32.jpg?1562055431"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Kolaghan's Command", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/c/7c884e1e-fecb-4330-b3de-5fc2a60f7173.jpg?1562788780", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/c/7c884e1e-fecb-4330-b3de-5fc2a60f7173.jpg?1562788780"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Kolaghan's Command", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/e/1e8bdd10-0bdc-4339-bd84-b540606438d6.jpg?1656000872", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/e/1e8bdd10-0bdc-4339-bd84-b540606438d6.jpg?1656000872"}, "reprint": true, "frame_effects": ["inverted"], "digital": false, "set_type": "masters"}, {"name": "Lash Out", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/e/de2c0c8b-5442-44fb-9686-d3dff5742501.jpg?1562371092", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/e/de2c0c8b-5442-44fb-9686-d3dff5742501.jpg?1562371092"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Lava Axe", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/0/807e5102-1fab-4ff4-aad8-94defbbb8a6b.jpg?1562241656", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/0/807e5102-1fab-4ff4-aad8-94defbbb8a6b.jpg?1562241656"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Lava Axe", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/1/e11ec278-46f5-4970-ad0b-f6718c73de6c.jpg?1562864233", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/1/e11ec278-46f5-4970-ad0b-f6718c73de6c.jpg?1562864233"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Lava Axe", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/e/fe6cff90-ecec-4610-82ea-0f2a109959cf.jpg?1562955255", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/e/fe6cff90-ecec-4610-82ea-0f2a109959cf.jpg?1562955255"}, "reprint": true, "digital": false, "set_type": "starter"}, {"name": "Lava Axe", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/2/f2bebbad-76aa-4388-891a-583e8af9509d.jpg?1562448334", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/2/f2bebbad-76aa-4388-891a-583e8af9509d.jpg?1562448334"}, "reprint": false, "digital": false, "set_type": "starter"}, {"name": "Lava Blister", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/d/cd0e9e53-2710-4c2a-a8e4-48f25375ebc7.jpg?1562933365", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/d/cd0e9e53-2710-4c2a-a8e4-48f25375ebc7.jpg?1562933365"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Lava Burst", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/9/79dc0e20-5790-4927-8432-cf0e9b7381d4.jpg?1562917534", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/9/79dc0e20-5790-4927-8432-cf0e9b7381d4.jpg?1562917534"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Lava Dart", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/1/b16dd041-451d-4914-8c46-aa315a90d802.jpg?1562201890", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/1/b16dd041-451d-4914-8c46-aa315a90d802.jpg?1562201890"}, "reprint": true, "digital": false, "set_type": "draft_innovation"}, {"name": "Lava Dart", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/6/865bb1d3-5b7d-40e9-87cc-96be9524a105.jpg?1562630775", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/6/865bb1d3-5b7d-40e9-87cc-96be9524a105.jpg?1562630775"}, "reprint": false, "frame_effects": ["tombstone"], "digital": false, "set_type": "expansion"}, {"name": "Lavalanche", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/4/749981d6-78e7-4f53-80a8-f211e61bd532.jpg?1562642149", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/4/749981d6-78e7-4f53-80a8-f211e61bd532.jpg?1562642149"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Lava Spike", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/9/79c21c1f-eaa4-454d-a1c7-b41466d0a428.jpg?1547517298", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/9/79c21c1f-eaa4-454d-a1c7-b41466d0a428.jpg?1547517298"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Lava Spike", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/0/60b2fae1-242b-45e0-a757-b1adc02c06f3.jpg?1562760596", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/0/60b2fae1-242b-45e0-a757-b1adc02c06f3.jpg?1562760596"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Lightning Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/3/83e3c502-9e3c-41db-806c-538243dc0453.jpg?1562241728", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/3/83e3c502-9e3c-41db-806c-538243dc0453.jpg?1562241728"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Lightning Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/3/63fec3f9-d399-48e6-84b6-c8410c24c382.jpg?1562054251", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/3/63fec3f9-d399-48e6-84b6-c8410c24c382.jpg?1562054251"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Lightning Bolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/e/ae5f9fb1-5a55-4db3-98a1-2628e3598c18.jpg?1648155765", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/e/ae5f9fb1-5a55-4db3-98a1-2628e3598c18.jpg?1648155765"}, "reprint": true, "digital": false, "set_type": "draft_innovation"}, {"name": "Lightning Bolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/3/435589bb-27c6-4a6d-9d63-394d5092b9d8.jpg?1561978182", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/3/435589bb-27c6-4a6d-9d63-394d5092b9d8.jpg?1561978182"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Lightning Bolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/5/d573ef03-4730-45aa-93dd-e45ac1dbaf4a.jpg?1559591645", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/5/d573ef03-4730-45aa-93dd-e45ac1dbaf4a.jpg?1559591645"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Lightning Bolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/8/c8c8390f-4072-454f-8dc4-174919187a47.jpg?1655641560", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/8/c8c8390f-4072-454f-8dc4-174919187a47.jpg?1655641560"}, "reprint": true, "frame_effects": ["inverted"], "digital": false, "set_type": "masters"}, {"name": "Lightning Bolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/6/c69f668b-cf28-495a-bbe1-24e9d0089fa1.jpg?1648155788", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/6/c69f668b-cf28-495a-bbe1-24e9d0089fa1.jpg?1648155788"}, "reprint": true, "frame_effects": ["showcase"], "digital": false, "set_type": "draft_innovation"}, {"name": "Lightning Bolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/7/27740ea5-79c8-420f-bc49-6d5eac58dac5.jpg?1657119952", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/7/27740ea5-79c8-420f-bc49-6d5eac58dac5.jpg?1657119952"}, "flavor_name": "Hadoken", "reprint": true, "digital": false, "set_type": "box"}, {"name": "Lightning Bolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/e/4eaac4fd-95f5-4f38-b593-0101e79a20f9.jpg?1623945607", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/e/4eaac4fd-95f5-4f38-b593-0101e79a20f9.jpg?1623945607"}, "reprint": true, "digital": false, "set_type": "masterpiece"}, {"name": "Lightning Bolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/5/45184cd7-b037-4a85-a063-e622ca928d17.jpg?1599352446", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/5/45184cd7-b037-4a85-a063-e622ca928d17.jpg?1599352446"}, "reprint": true, "digital": false, "set_type": "box"}, {"name": "Lightning Bolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/f/6fb94c1b-8002-4d79-add0-c4dfef9019ee.jpg?1599352358", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/f/6fb94c1b-8002-4d79-add0-c4dfef9019ee.jpg?1599352358"}, "reprint": true, "digital": false, "set_type": "box"}, {"name": "Lightning Bolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/a/6ab06973-6440-4b12-8947-8c412500fa41.jpg?1599352361", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/a/6ab06973-6440-4b12-8947-8c412500fa41.jpg?1599352361"}, "reprint": true, "digital": false, "set_type": "box"}, {"name": "Lightning Bolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/3/c3eb3895-b64c-46ab-b704-3c46963920ba.jpg?1599352414", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/3/c3eb3895-b64c-46ab-b704-3c46963920ba.jpg?1599352414"}, "reprint": true, "digital": false, "set_type": "box"}, {"name": "Lightning Bolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/f/ff204024-20a5-4bb9-82b6-f6b4337efd60.jpg?1552226335", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/f/ff204024-20a5-4bb9-82b6-f6b4337efd60.jpg?1552226335"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Lightning Bolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/8/28708c8c-4336-4d04-b43a-59a31471a9f6.jpg?1561756817", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/8/28708c8c-4336-4d04-b43a-59a31471a9f6.jpg?1561756817"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Lightning Helix", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/1/613789fe-fac1-4200-b0a1-c84d1fa27cff.jpg?1562917870", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/1/613789fe-fac1-4200-b0a1-c84d1fa27cff.jpg?1562917870"}, "reprint": true, "digital": false, "set_type": "duel_deck"}, {"name": "Lightning Helix", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/b/1b2ecf55-c1cc-4b28-b7ce-e1b25305155e.jpg?1598917140", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/b/1b2ecf55-c1cc-4b28-b7ce-e1b25305155e.jpg?1598917140"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Lightning Helix", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/2/227ac87a-7196-40d0-ab00-98ebafcca09a.jpg?1624065725", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/2/227ac87a-7196-40d0-ab00-98ebafcca09a.jpg?1624065725"}, "reprint": true, "digital": false, "set_type": "masterpiece"}, {"name": "Lightning Helix", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/e/4ec9e67b-1b4e-4e4e-9758-be697d308f16.jpg?1561757108", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/e/4ec9e67b-1b4e-4e4e-9758-be697d308f16.jpg?1561757108"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Lightning Javelin", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/1/c1ccaeed-9670-4432-8a45-d5c06119fa9f.jpg?1562040115", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/1/c1ccaeed-9670-4432-8a45-d5c06119fa9f.jpg?1562040115"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Lightning Storm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/9/c9c0388e-a04c-4757-a06d-8e8046f5a783.jpg?1593275279", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/9/c9c0388e-a04c-4757-a06d-8e8046f5a783.jpg?1593275279"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Lightning Strike", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/0/f0f55dee-7e39-4183-8e74-844d9c299bf5.jpg?1562566447", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/0/f0f55dee-7e39-4183-8e74-844d9c299bf5.jpg?1562566447"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Lightning Strike", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/b/bbb03f2e-2b92-4aa1-afae-301ed5d151d3.jpg?1562827848", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/b/bbb03f2e-2b92-4aa1-afae-301ed5d151d3.jpg?1562827848"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Lightning Surge", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/4/0452d78d-eafc-4ccb-a478-d1f46bcefffe.jpg?1562628459", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/4/0452d78d-eafc-4ccb-a478-d1f46bcefffe.jpg?1562628459"}, "reprint": false, "frame_effects": ["tombstone"], "digital": false, "set_type": "expansion"}, {"name": "Light Up the Night", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/d/7de68154-3b82-4a94-98a6-cfc49d359e4e.jpg?1636223152", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/d/7de68154-3b82-4a94-98a6-cfc49d359e4e.jpg?1636223152"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Lorehold Command", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/4/e4f0885f-1049-4a19-853d-f4e6d4bec29e.jpg?1627429447", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/4/e4f0885f-1049-4a19-853d-f4e6d4bec29e.jpg?1627429447"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Lunge", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/9/e9e43349-429c-43f7-b808-c4bf37370a9f.jpg?1562383530", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/9/e9e43349-429c-43f7-b808-c4bf37370a9f.jpg?1562383530"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Magma Burst", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/9/d9752bc3-0bdf-4657-8750-73c8cbc8e83f.jpg?1562940942", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/9/d9752bc3-0bdf-4657-8750-73c8cbc8e83f.jpg?1562940942"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Magma Jet", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/a/8af1c5b0-973d-467e-a797-51ca75c183c1.jpg?1593813497", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/a/8af1c5b0-973d-467e-a797-51ca75c183c1.jpg?1593813497"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Magma Jet", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/1/51ea1728-08aa-4553-90b2-919c70712ed5.jpg?1562877009", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/1/51ea1728-08aa-4553-90b2-919c70712ed5.jpg?1562877009"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Magma Jet", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/e/be7552ac-4546-492d-8d11-d6678a04b9c3.jpg?1562640021", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/e/be7552ac-4546-492d-8d11-d6678a04b9c3.jpg?1562640021"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Make Mischief", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/2/a2049072-5901-4edd-8305-ce55f256bca5.jpg?1576384624", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/2/a2049072-5901-4edd-8305-ce55f256bca5.jpg?1576384624"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Melt Terrain", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/d/1d94a1d1-6d24-46e1-9568-42e1a810ad31.jpg?1562815251", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/d/1d94a1d1-6d24-46e1-9568-42e1a810ad31.jpg?1562815251"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Mindblaze", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/9/59418766-5567-4ec4-af1f-1cb2db2958d0.jpg?1562760146", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/9/59418766-5567-4ec4-af1f-1cb2db2958d0.jpg?1562760146"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Mindswipe", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/5/557e8303-a021-4257-b41a-7d25f04618c8.jpg?1562786781", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/5/557e8303-a021-4257-b41a-7d25f04618c8.jpg?1562786781"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Misfortune", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/1/b14cc32a-eb4f-4690-aceb-160780743ebe.jpg?1562770145", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/1/b14cc32a-eb4f-4690-aceb-160780743ebe.jpg?1562770145"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Molten Disaster", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/1/31e0713c-dbf4-4403-ae69-58fd483e2481.jpg?1562905110", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/1/31e0713c-dbf4-4403-ae69-58fd483e2481.jpg?1562905110"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Molten Influence", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/c/4c2b326b-d177-4a03-a0a3-fe2c2d4af272.jpg?1562908953", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/c/4c2b326b-d177-4a03-a0a3-fe2c2d4af272.jpg?1562908953"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Molten Rain", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/c/ecdd414b-3d9d-4347-acce-289209d09fc4.jpg?1593813519", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/c/ecdd414b-3d9d-4347-acce-289209d09fc4.jpg?1593813519"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Molten Rain", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/8/f888b4d4-31f9-4322-8225-4d7e7a9f4dd5.jpg?1562163535", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/8/f888b4d4-31f9-4322-8225-4d7e7a9f4dd5.jpg?1562163535"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Moonrager's Slash", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/b/eb4f266b-c41c-4047-ae6f-b2226c7459e8.jpg?1636223196", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/b/eb4f266b-c41c-4047-ae6f-b2226c7459e8.jpg?1636223196"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Needle Drop", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/3/d3f89bcf-46f8-4598-a949-7f10134606aa.jpg?1562369628", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/3/d3f89bcf-46f8-4598-a949-7f10134606aa.jpg?1562369628"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Neonate's Rush", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/e/dee17e12-e08f-4449-9f49-05f20e0d1670.jpg?1636223275", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/e/dee17e12-e08f-4449-9f49-05f20e0d1670.jpg?1636223275"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Nerf War", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/e/2eb08fc5-29a4-4911-ac94-dc5ff2fc2ace.jpg?1561756860", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/e/2eb08fc5-29a4-4911-ac94-dc5ff2fc2ace.jpg?1561756860"}, "reprint": false, "digital": false, "set_type": "funny"}, {"name": "Open Fire", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/4/448f9fb5-ffb5-4325-9f81-ce8782e5f9e9.jpg?1562797508", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/4/448f9fb5-ffb5-4325-9f81-ce8782e5f9e9.jpg?1562797508"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Orcish Cannonade", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/a/0afae574-aa96-4500-9882-a4b10337b6f5.jpg?1619397326", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/a/0afae574-aa96-4500-9882-a4b10337b6f5.jpg?1619397326"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Orcish Cannonade", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/e/4e40f99c-9608-4463-8c6f-c6e142f0d716.jpg?1562911399", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/e/4e40f99c-9608-4463-8c6f-c6e142f0d716.jpg?1562911399"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Parallectric Feedback", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/9/891f1d29-377a-4f71-917f-ff10e785caee.jpg?1593272344", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/9/891f1d29-377a-4f71-917f-ff10e785caee.jpg?1593272344"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Parch", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/3/d3ab8065-cecc-4b19-be93-7cf791a93e62.jpg?1562864229", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/3/d3ab8065-cecc-4b19-be93-7cf791a93e62.jpg?1562864229"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Pass the Torch", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/d/5dc05455-4ebd-46f8-94cf-14f0d5420037.jpg?1654043945", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/d/5dc05455-4ebd-46f8-94cf-14f0d5420037.jpg?1654043945"}, "reprint": false, "digital": true, "set_type": "alchemy"}, {"name": "Peak Eruption", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/d/ed0a00f7-aee0-4ab2-bab6-bc0949176a7a.jpg?1562836713", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/d/ed0a00f7-aee0-4ab2-bab6-bc0949176a7a.jpg?1562836713"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Pigment Storm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/2/d285a7a1-bb7e-4a78-a49f-c2add62b829a.jpg?1624592111", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/2/d285a7a1-bb7e-4a78-a49f-c2add62b829a.jpg?1624592111"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Pillar of Flame", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/9/c983e879-d9d2-47cc-9958-506711ca80cd.jpg?1592709165", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/9/c983e879-d9d2-47cc-9958-506711ca80cd.jpg?1592709165"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Pillar of Flame", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/3/c39677b8-9a43-4e62-a83a-4a9d6372310b.jpg?1562640029", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/3/c39677b8-9a43-4e62-a83a-4a9d6372310b.jpg?1562640029"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Play with Fire", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/2/42901bec-a8d0-46a3-a710-bfb7bd87f155.jpg?1640721639", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/2/42901bec-a8d0-46a3-a710-bfb7bd87f155.jpg?1640721639"}, "reprint": false, "frame_effects": ["inverted"], "digital": false, "set_type": "expansion"}, {"name": "Poison the Well", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/b/cb86eeec-d50f-4823-86bd-35437926a6e4.jpg?1562835997", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/b/cb86eeec-d50f-4823-86bd-35437926a6e4.jpg?1562835997"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Precision Bolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/5/a59b4e5b-e9e0-4507-b9e7-8fba7e3a54f9.jpg?1572894271", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/5/a59b4e5b-e9e0-4507-b9e7-8fba7e3a54f9.jpg?1572894271"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Prismari Command", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/6/866b7fd4-86e3-4b42-b1ea-33bad0db1f9f.jpg?1627429955", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/6/866b7fd4-86e3-4b42-b1ea-33bad0db1f9f.jpg?1627429955"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Prophetic Bolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/0/101163c0-cd2f-4e1a-84b3-f64fc748807d.jpg?1592713462", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/0/101163c0-cd2f-4e1a-84b3-f64fc748807d.jpg?1592713462"}, "reprint": true, "digital": false, "set_type": "duel_deck"}, {"name": "Prophetic Bolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/9/79f74291-c452-4a60-bf5f-73efad6583d4.jpg?1562923762", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/9/79f74291-c452-4a60-bf5f-73efad6583d4.jpg?1562923762"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Provoke the Trolls", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/7/2727b05a-0c86-4c59-b7b4-425bdd8e775d.jpg?1631049503", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/7/2727b05a-0c86-4c59-b7b4-425bdd8e775d.jpg?1631049503"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Pulse of the Forge", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/a/ea3ed9c8-b552-4a9a-b77a-8b148638b4f0.jpg?1562640294", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/a/ea3ed9c8-b552-4a9a-b77a-8b148638b4f0.jpg?1562640294"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Puncture Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/b/3bf90b4d-98cf-4953-b6ae-c41d21ab559b.jpg?1562907480", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/b/3bf90b4d-98cf-4953-b6ae-c41d21ab559b.jpg?1562907480"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Punishing Fire", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/d/0da4409b-fe3f-4500-bf4b-890593f7d313.jpg?1562897775", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/d/0da4409b-fe3f-4500-bf4b-890593f7d313.jpg?1562897775"}, "reprint": true, "digital": false, "set_type": "duel_deck"}, {"name": "Punishing Fire", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/6/56e76f1c-5a07-455a-a3df-4c45b5b25b82.jpg?1562612350", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/6/56e76f1c-5a07-455a-a3df-4c45b5b25b82.jpg?1562612350"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Punish the Enemy", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/1/4179a72b-8482-46ec-9815-f5d6d94b5aa5.jpg?1562907014", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/1/4179a72b-8482-46ec-9815-f5d6d94b5aa5.jpg?1562907014"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Pyromatics", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/2/c22c9dab-e8d5-48b3-8fd2-9f4138ee0c7c.jpg?1593272350", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/2/c22c9dab-e8d5-48b3-8fd2-9f4138ee0c7c.jpg?1593272350"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Quenchable Fire", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/e/ee1c0ded-2a80-4ed4-b9fc-3a18bf5c3239.jpg?1562804519", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/e/ee1c0ded-2a80-4ed4-b9fc-3a18bf5c3239.jpg?1562804519"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Rain of Embers", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/d/2d5391a9-6c30-4f9b-b746-a4427a3e63fc.jpg?1598915805", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/d/2d5391a9-6c30-4f9b-b746-a4427a3e63fc.jpg?1598915805"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Rakdos Charm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/f/0fcd4394-d22d-4eec-ad73-ffaf10ad60de.jpg?1562782720", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/f/0fcd4394-d22d-4eec-ad73-ffaf10ad60de.jpg?1562782720"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Rakdos's Return", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/7/d72981c0-1632-4d64-9341-2a76047d9b36.jpg?1562793869", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/7/d72981c0-1632-4d64-9341-2a76047d9b36.jpg?1562793869"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Ral's Outburst", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/b/6be3dd3e-50d2-4729-9caa-b2cd984f4c97.jpg?1557577237", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/b/6be3dd3e-50d2-4729-9caa-b2cd984f4c97.jpg?1557577237"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Ravaging Blaze", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/d/5d9404b2-f0ea-4a31-bc7b-6748574c57d3.jpg?1562021972", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/d/5d9404b2-f0ea-4a31-bc7b-6748574c57d3.jpg?1562021972"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Reality Hemorrhage", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/0/c044168d-cb08-493d-98c1-b66b6149fe5a.jpg?1562933647", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/0/c044168d-cb08-493d-98c1-b66b6149fe5a.jpg?1562933647"}, "reprint": false, "frame_effects": ["devoid"], "digital": false, "set_type": "expansion"}, {"name": "Reckless Abandon", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/f/8f335d43-cacb-40ad-93c1-9a861e9f66c7.jpg?1562444699", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/f/8f335d43-cacb-40ad-93c1-9a861e9f66c7.jpg?1562444699"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Red Sun's Zenith", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/7/373eb109-0e30-41c1-b2df-6bc78d968890.jpg?1562610602", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/7/373eb109-0e30-41c1-b2df-6bc78d968890.jpg?1562610602"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Rekindled Flame", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/3/131c6377-4ed4-4a76-a9cb-be7ad17d76fd.jpg?1562899037", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/3/131c6377-4ed4-4a76-a9cb-be7ad17d76fd.jpg?1562899037"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Release the Ants", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/b/1b6f1afb-2451-4611-ac3e-3513a4651719.jpg?1562877157", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/b/1b6f1afb-2451-4611-ac3e-3513a4651719.jpg?1562877157"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Rending Flame", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/1/51332c31-41df-4379-aa63-6a734a4df618.jpg?1643591905", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/1/51332c31-41df-4379-aa63-6a734a4df618.jpg?1643591905"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Repeating Barrage", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/a/ba90a2d6-8292-4ff1-91d0-b30ae9775f12.jpg?1562562987", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/a/ba90a2d6-8292-4ff1-91d0-b30ae9775f12.jpg?1562562987"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Resounding Thunder", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/8/680b7955-d939-4195-aba8-b46a8c925616.jpg?1562704894", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/8/680b7955-d939-4195-aba8-b46a8c925616.jpg?1562704894"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Rhystic Lightning", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/1/21ce365e-3002-42e9-aeb5-1b845408271e.jpg?1562901251", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/1/21ce365e-3002-42e9-aeb5-1b845408271e.jpg?1562901251"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Rift Bolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/8/88dde96e-6824-4d26-9fb5-86b9f3c50959.jpg?1562923901", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/8/88dde96e-6824-4d26-9fb5-86b9f3c50959.jpg?1562923901"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Rift Bolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/5/25176fe7-a5a0-44d2-9619-193063c55902.jpg?1561929531", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/5/25176fe7-a5a0-44d2-9619-193063c55902.jpg?1561929531"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Risk Factor", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/e/4eda89d9-9bd1-4a55-ac02-f9a0625d8e5b.jpg?1572893240", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/e/4eda89d9-9bd1-4a55-ac02-f9a0625d8e5b.jpg?1572893240"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Roil Eruption", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/6/86572747-8faa-4242-b059-07d11e6be1cd.jpg?1604197631", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/6/86572747-8faa-4242-b059-07d11e6be1cd.jpg?1604197631"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Roiling Terrain", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/7/87d3a425-01d1-4001-92f9-8e297dd862b7.jpg?1562291349", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/7/87d3a425-01d1-4001-92f9-8e297dd862b7.jpg?1562291349"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Rolling Earthquake", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/c/3c1bf210-ecdb-4b49-8504-51360c269e66.jpg?1562256070", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/c/3c1bf210-ecdb-4b49-8504-51360c269e66.jpg?1562256070"}, "reprint": false, "digital": false, "set_type": "starter"}, {"name": "Sacred Fire", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/0/c0b1dcd7-6dd9-4134-bc6c-9dc7754006a2.jpg?1636684880", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/0/c0b1dcd7-6dd9-4134-bc6c-9dc7754006a2.jpg?1636684880"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Sarkhan's Catharsis", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/f/2f4b6f26-c66b-4048-9503-af0a886ef14f.jpg?1557576811", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/f/2f4b6f26-c66b-4048-9503-af0a886ef14f.jpg?1557576811"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Sarkhan's Dragonfire", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/9/b96a7320-089a-4a7e-813f-49ca1620df76.jpg?1562303870", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/9/b96a7320-089a-4a7e-813f-49ca1620df76.jpg?1562303870"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Sarkhan's Rage", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/7/4787924f-3186-4e18-b53c-dd67c5f42220.jpg?1562785591", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/7/4787924f-3186-4e18-b53c-dd67c5f42220.jpg?1562785591"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Saut\u00e9", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/5/85cbebbb-7ea4-4140-933f-186cad08697d.jpg?1562488867", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/5/85cbebbb-7ea4-4140-933f-186cad08697d.jpg?1562488867"}, "reprint": false, "digital": false, "set_type": "funny"}, {"name": "Scent of Cinder", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/0/c030eca0-bc5f-403b-8600-1f295fc85fee.jpg?1562445189", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/0/c030eca0-bc5f-403b-8600-1f295fc85fee.jpg?1562445189"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Scent of Cinder", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/0/d083fcdc-1e1f-4ad3-82d1-11f0b84dd74d.jpg?1562934001", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/0/d083fcdc-1e1f-4ad3-82d1-11f0b84dd74d.jpg?1562934001"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Scorching Lava", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/a/2a85437f-052e-494c-a9ee-265c4624a409.jpg?1562903659", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/a/2a85437f-052e-494c-a9ee-265c4624a409.jpg?1562903659"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Scorching Missile", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/6/0672960b-4cb5-4ed6-ba3c-6b97290e0330.jpg?1562896294", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/6/0672960b-4cb5-4ed6-ba3c-6b97290e0330.jpg?1562896294"}, "reprint": false, "frame_effects": ["tombstone"], "digital": false, "set_type": "expansion"}, {"name": "Scorching Spear", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/e/9e4817bd-68e8-4a85-983a-ee6dda2bbf33.jpg?1562447352", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/e/9e4817bd-68e8-4a85-983a-ee6dda2bbf33.jpg?1562447352"}, "reprint": false, "digital": false, "set_type": "starter"}, {"name": "Searing Barrage", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/2/d2f11135-e9ce-4e4c-bea7-72a46d326e40.jpg?1572490453", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/2/d2f11135-e9ce-4e4c-bea7-72a46d326e40.jpg?1572490453"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Searing Blaze", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/0/301f13dd-39b8-4a93-9c05-3dc4fa1f1c75.jpg?1562284687", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/0/301f13dd-39b8-4a93-9c05-3dc4fa1f1c75.jpg?1562284687"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Searing Blaze", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/7/974c599b-170e-4948-b741-47f61c769b6e.jpg?1561757630", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/7/974c599b-170e-4948-b741-47f61c769b6e.jpg?1561757630"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Searing Blood", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/b/bb43fd07-d281-447d-88bf-c53498c2cf20.jpg?1593092367", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/b/bb43fd07-d281-447d-88bf-c53498c2cf20.jpg?1593092367"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Searing Flesh", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/8/d83db110-42e7-4823-a686-b83205faf503.jpg?1562946564", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/8/d83db110-42e7-4823-a686-b83205faf503.jpg?1562946564"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Searing Spear", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/1/11a94b7c-0216-473c-87a6-71e5a64d7799.jpg?1562550529", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/1/11a94b7c-0216-473c-87a6-71e5a64d7799.jpg?1562550529"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Searing Spear", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/5/e574f1f8-ca61-43b4-8230-2636709a3855.jpg?1562497262", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/5/e574f1f8-ca61-43b4-8230-2636709a3855.jpg?1562497262"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Searing Touch", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/9/e9091667-d5a8-4978-9023-032ff65f9642.jpg?1562057345", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/9/e9091667-d5a8-4978-9023-032ff65f9642.jpg?1562057345"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Searing Wind", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/b/7b761f97-3690-497a-b6ab-c71f61b8e841.jpg?1562917793", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/b/7b761f97-3690-497a-b6ab-c71f61b8e841.jpg?1562917793"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Seismic Wave", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/5/e55b8ffb-c2e4-4676-9051-ff6c686cad0b.jpg?1654567822", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/5/e55b8ffb-c2e4-4676-9051-ff6c686cad0b.jpg?1654567822"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Shard Volley", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/3/43db4810-078e-487a-afef-57cbc1db0cc7.jpg?1562878159", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/3/43db4810-078e-487a-afef-57cbc1db0cc7.jpg?1562878159"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Shock", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/0/00365412-41db-427c-9109-8f69c17c326d.jpg?1576381909", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/0/00365412-41db-427c-9109-8f69c17c326d.jpg?1576381909"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Shock", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/3/334ad39a-4088-4530-8f3c-d34e7cc99fae.jpg?1562545881", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/3/334ad39a-4088-4530-8f3c-d34e7cc99fae.jpg?1562545881"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Shock", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/3/83c92b5d-103c-4719-a850-690a7010291a.jpg?1562926054", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/3/83c92b5d-103c-4719-a850-690a7010291a.jpg?1562926054"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Shock", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/a/ea653772-a5fe-4416-bef3-fd41133371db.jpg?1562250385", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/a/ea653772-a5fe-4416-bef3-fd41133371db.jpg?1562250385"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Shock", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/9/f9b2ff2a-6dfe-4635-8da2-22d525e82b94.jpg?1562597849", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/9/f9b2ff2a-6dfe-4635-8da2-22d525e82b94.jpg?1562597849"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Shock", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/0/60eeb025-704c-4a82-90b2-f91202ae30d9.jpg?1623945691", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/0/60eeb025-704c-4a82-90b2-f91202ae30d9.jpg?1623945691"}, "reprint": true, "digital": false, "set_type": "masterpiece"}, {"name": "Shower of Sparks", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/4/54428999-a83d-40a5-9753-dfefdf705a9e.jpg?1562912591", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/4/54428999-a83d-40a5-9753-dfefdf705a9e.jpg?1562912591"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Shrapnel Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/5/056affab-4e2a-4b68-b864-d879becd3c45.jpg?1562134669", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/5/056affab-4e2a-4b68-b864-d879becd3c45.jpg?1562134669"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Shrapnel Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/8/f8baa9d1-b45d-4947-9eb6-7f580c83a2c3.jpg?1562164853", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/8/f8baa9d1-b45d-4947-9eb6-7f580c83a2c3.jpg?1562164853"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Sizzle", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/f/dfdfe2a9-1323-4f15-b2ce-d8dd404b914d.jpg?1587913602", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/f/dfdfe2a9-1323-4f15-b2ce-d8dd404b914d.jpg?1587913602"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Sizzle", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/1/f1ca1eee-d97d-48c6-84f1-7d1f972c3ca9.jpg?1562383987", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/1/f1ca1eee-d97d-48c6-84f1-7d1f972c3ca9.jpg?1562383987"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Skewer the Critics", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/7/97295660-6bea-46ae-9a3b-0fc6abba407f.jpg?1584831035", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/7/97295660-6bea-46ae-9a3b-0fc6abba407f.jpg?1584831035"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Skullcrack", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/0/8068a146-f6fe-46f3-a42e-822fbc3502e6.jpg?1561833692", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/0/8068a146-f6fe-46f3-a42e-822fbc3502e6.jpg?1561833692"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Skull Rend", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/c/1c8efb23-bac0-41d2-b4ee-27a6b1fe3134.jpg?1562783397", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/c/1c8efb23-bac0-41d2-b4ee-27a6b1fe3134.jpg?1562783397"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Skullscorch", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/8/88f1343c-77bf-4f44-8226-fdfb2c2c7015.jpg?1562630775", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/8/88f1343c-77bf-4f44-8226-fdfb2c2c7015.jpg?1562630775"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Slagstorm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/e/9e318b03-2aad-462b-a2a9-8b6bdf0e93d6.jpg?1562613393", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/e/9e318b03-2aad-462b-a2a9-8b6bdf0e93d6.jpg?1562613393"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Slaying Fire", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/3/83b5b110-c430-4ffe-9fc1-8e6987f52d1e.jpg?1572490469", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/3/83b5b110-c430-4ffe-9fc1-8e6987f52d1e.jpg?1572490469"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Smash to Smithereens", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/e/7eda1524-44dd-4f70-ac21-bac51578860e.jpg?1562832260", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/e/7eda1524-44dd-4f70-ac21-bac51578860e.jpg?1562832260"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Smash to Smithereens", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/d/4daccff6-8395-4b11-a4ce-3576aa38bc09.jpg?1562636800", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/d/4daccff6-8395-4b11-a4ce-3576aa38bc09.jpg?1562636800"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Smoke Spirits' Aid", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/4/e492a245-46ba-438e-8d81-4626faa49bff.jpg?1651655377", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/4/e492a245-46ba-438e-8d81-4626faa49bff.jpg?1651655377"}, "reprint": false, "digital": false, "set_type": "commander"}, {"name": "Solar Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/3/b36fc40c-6a68-4192-91d9-2031c7d32e05.jpg?1562937333", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/3/b36fc40c-6a68-4192-91d9-2031c7d32e05.jpg?1562937333"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Sonic Assault", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/c/cc61a398-cf16-415b-b3cf-897217dc7cc9.jpg?1572893813", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/c/cc61a398-cf16-415b-b3cf-897217dc7cc9.jpg?1572893813"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Sonic Burst", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/5/05530d5a-dcb6-403e-9e35-224c7b5cf615.jpg?1562086889", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/5/05530d5a-dcb6-403e-9e35-224c7b5cf615.jpg?1562086889"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Sonic Seizure", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/8/98eb9371-aa20-4790-baf8-a1ad95de39de.jpg?1562631090", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/8/98eb9371-aa20-4790-baf8-a1ad95de39de.jpg?1562631090"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Spark Jolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/e/6ee479c2-a115-450b-bc2e-b03d23b82f2d.jpg?1562819617", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/e/6ee479c2-a115-450b-bc2e-b03d23b82f2d.jpg?1562819617"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Spark Spray", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/6/f60d8716-4297-484c-8e02-c30ce2773a65.jpg?1562536945", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/6/f60d8716-4297-484c-8e02-c30ce2773a65.jpg?1562536945"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Spawning Breath", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/0/90ec1540-e8cb-4edc-a3b3-f71423cb46fc.jpg?1562706335", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/0/90ec1540-e8cb-4edc-a3b3-f71423cb46fc.jpg?1562706335"}, "reprint": false, "digital": false, "set_type": "expansion"}]}
\ No newline at end of file
diff --git a/web/public/mtg/jsons/burn3.json b/web/public/mtg/jsons/burn3.json
new file mode 100644
index 00000000..aa261ae5
--- /dev/null
+++ b/web/public/mtg/jsons/burn3.json
@@ -0,0 +1 @@
+{"has_more": false, "data": [{"name": "Staggershock", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/5/75624ab3-ddbd-4fe8-8a07-7d1f78ec8a84.jpg?1562705194", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/5/75624ab3-ddbd-4fe8-8a07-7d1f78ec8a84.jpg?1562705194"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Starfall", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/3/13921f0d-f163-4275-b025-045c1ccd99e5.jpg?1593096122", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/3/13921f0d-f163-4275-b025-045c1ccd99e5.jpg?1593096122"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Start from Scratch", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/5/55c99486-ae64-4293-81fb-a4b02e8fcae6.jpg?1637082383", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/5/55c99486-ae64-4293-81fb-a4b02e8fcae6.jpg?1637082383"}, "reprint": false, "frame_effects": ["lesson"], "digital": false, "set_type": "expansion"}, {"name": "Steam Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/4/144a1b4e-d960-4c3a-810b-11a0c78635ad.jpg?1562899291", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/4/144a1b4e-d960-4c3a-810b-11a0c78635ad.jpg?1562899291"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Stoke the Flames", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/d/1d94c000-52e0-4215-83af-6351dc43e636.jpg?1562783509", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/d/1d94c000-52e0-4215-83af-6351dc43e636.jpg?1562783509"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Stoke the Flames", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/5/f5a1071a-c50c-439f-8387-5b2c143e24e4.jpg?1562640134", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/5/f5a1071a-c50c-439f-8387-5b2c143e24e4.jpg?1562640134"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Stomping Slabs", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/2/820f1acf-7f0c-4ee5-9f18-b5627aac7c81.jpg?1562879653", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/2/820f1acf-7f0c-4ee5-9f18-b5627aac7c81.jpg?1562879653"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Strategy, Schmategy", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/2/a2996a63-9fb6-4455-906d-13f917a8bb29.jpg?1562799134", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/2/a2996a63-9fb6-4455-906d-13f917a8bb29.jpg?1562799134"}, "reprint": false, "digital": false, "set_type": "funny"}, {"name": "Structural Collapse", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/1/d10da484-db67-4afc-90ef-6caf7d2e3a75.jpg?1561847167", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/1/d10da484-db67-4afc-90ef-6caf7d2e3a75.jpg?1561847167"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Structural Distortion", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/7/a7895890-a774-4c7c-9f15-78b8aadfd9ef.jpg?1576384931", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/7/a7895890-a774-4c7c-9f15-78b8aadfd9ef.jpg?1576384931"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Sudden Shock", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/f/9fcc7ad0-1348-44e9-9782-e9b7fd032fa4.jpg?1606761799", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/f/9fcc7ad0-1348-44e9-9782-e9b7fd032fa4.jpg?1606761799"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Sulfurous Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/7/67511e0e-be09-4f4e-9949-b9ecbdc7f536.jpg?1562916599", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/7/67511e0e-be09-4f4e-9949-b9ecbdc7f536.jpg?1562916599"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Surging Flame", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/5/156994b5-a0f2-4d02-9bda-882e80d9905c.jpg?1561756701", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/5/156994b5-a0f2-4d02-9bda-882e80d9905c.jpg?1561756701"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Tarfire", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/1/d13a898e-6a97-4fd9-980e-3bfd8d755386.jpg?1562369172", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/1/d13a898e-6a97-4fd9-980e-3bfd8d755386.jpg?1562369172"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Thunderblade Charge", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/8/88a85be1-9de5-4f96-9fd1-15f3f17c4bea.jpg?1562922621", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/8/88a85be1-9de5-4f96-9fd1-15f3f17c4bea.jpg?1562922621"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Thunderbolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/8/5845a5bc-6b7d-4bbb-80b3-a0f877b95553.jpg?1592709223", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/8/5845a5bc-6b7d-4bbb-80b3-a0f877b95553.jpg?1592709223"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Thunderbolt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/0/a0a4b641-2eb3-482b-91a1-236ebe2a7a41.jpg?1562802418", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/0/a0a4b641-2eb3-482b-91a1-236ebe2a7a41.jpg?1562802418"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Thunderous Wrath", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/a/daa39826-7f89-41cb-a7fe-7f7be817d5cd.jpg?1592709229", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/a/daa39826-7f89-41cb-a7fe-7f7be817d5cd.jpg?1592709229"}, "reprint": false, "frame_effects": ["miracle"], "digital": false, "set_type": "expansion"}, {"name": "Titan's Revenge", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/1/b1b0f9ca-b752-4dd6-982b-06bb3a27ddbc.jpg?1562880793", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/1/b1b0f9ca-b752-4dd6-982b-06bb3a27ddbc.jpg?1562880793"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Touch of the Void", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/0/006ead4a-dc57-4856-8e13-235ba55483e6.jpg?1562895118", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/0/006ead4a-dc57-4856-8e13-235ba55483e6.jpg?1562895118"}, "reprint": false, "frame_effects": ["devoid"], "digital": false, "set_type": "expansion"}, {"name": "Traitor's Roar", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/5/751e2700-6425-45b8-b026-8c78098f08b2.jpg?1562831801", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/5/751e2700-6425-45b8-b026-8c78098f08b2.jpg?1562831801"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Tribal Flames", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/7/07fafa53-1e22-43f5-abf3-bbab8130f84d.jpg?1561966002", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/7/07fafa53-1e22-43f5-abf3-bbab8130f84d.jpg?1561966002"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Tribal Flames", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/b/9b32531e-c759-4603-abd0-1724e8df70db.jpg?1562926326", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/b/9b32531e-c759-4603-abd0-1724e8df70db.jpg?1562926326"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Unfriendly Fire", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/a/7a61b274-0499-4cb6-a2e4-f5e18ad7fd2d.jpg?1562558512", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/a/7a61b274-0499-4cb6-a2e4-f5e18ad7fd2d.jpg?1562558512"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Unlicensed Disintegration", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/6/16ad8f86-7860-4896-a161-07bf347bbd5b.jpg?1576382889", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/6/16ad8f86-7860-4896-a161-07bf347bbd5b.jpg?1576382889"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Unlicensed Disintegration", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/4/74843584-d6b1-4ee6-bedb-999ab0a42bb9.jpg?1562636815", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/4/74843584-d6b1-4ee6-bedb-999ab0a42bb9.jpg?1562636815"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Urza's Rage", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/7/774c52e2-b0d1-4b70-b6d1-bf98f6298603.jpg?1562917055", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/7/774c52e2-b0d1-4b70-b6d1-bf98f6298603.jpg?1562917055"}, "reprint": true, "digital": false, "set_type": "duel_deck"}, {"name": "Urza's Rage", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/1/61a25a35-3ae4-471e-adcd-d8baf2f77b68.jpg?1562914759", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/1/61a25a35-3ae4-471e-adcd-d8baf2f77b68.jpg?1562914759"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Urza's Rage", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/8/d80e9897-d84c-4992-9e8e-3a00f377c7e5.jpg?1623945800", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/8/d80e9897-d84c-4992-9e8e-3a00f377c7e5.jpg?1623945800"}, "reprint": true, "digital": false, "set_type": "masterpiece"}, {"name": "Volcanic Fallout", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/5/65536d12-e75c-42b5-b592-a3ad4f550a71.jpg?1592485188", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/5/65536d12-e75c-42b5-b592-a3ad4f550a71.jpg?1592485188"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Volcanic Fallout", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/d/8d3a69d2-518d-4b70-a03e-6d02a525f9ad.jpg?1561757550", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/d/8d3a69d2-518d-4b70-a03e-6d02a525f9ad.jpg?1561757550"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Volcanic Geyser", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/f/df5bab70-3c28-48db-9ed3-64706f64f4fa.jpg?1562560984", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/f/df5bab70-3c28-48db-9ed3-64706f64f4fa.jpg?1562560984"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Volcanic Geyser", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/1/911787db-9023-46f8-9501-3ad26b6ca51d.jpg?1562720483", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/1/911787db-9023-46f8-9501-3ad26b6ca51d.jpg?1562720483"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Volcanic Hammer", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/8/f8d93606-4864-4a5f-bcbf-8638211e979d.jpg?1562251759", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/8/f8d93606-4864-4a5f-bcbf-8638211e979d.jpg?1562251759"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Volcanic Hammer", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/8/58c0489d-b073-4ad4-b044-447fcc865b6c.jpg?1562915903", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/8/58c0489d-b073-4ad4-b044-447fcc865b6c.jpg?1562915903"}, "reprint": true, "digital": false, "set_type": "starter"}, {"name": "Volcanic Hammer", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/5/9563d7c1-4ed1-4919-b0b8-ea1ec9d4bbf6.jpg?1562447337", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/5/9563d7c1-4ed1-4919-b0b8-ea1ec9d4bbf6.jpg?1562447337"}, "reprint": false, "digital": false, "set_type": "starter"}, {"name": "Volcanic Spray", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/7/97daab4b-d934-4a3f-a043-f7c9c1dd32bf.jpg?1562923217", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/7/97daab4b-d934-4a3f-a043-f7c9c1dd32bf.jpg?1562923217"}, "reprint": false, "frame_effects": ["tombstone"], "digital": false, "set_type": "expansion"}, {"name": "Volt Charge", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/a/aa88011c-a19d-4faa-8da6-86b9980cd571.jpg?1562880613", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/a/aa88011c-a19d-4faa-8da6-86b9980cd571.jpg?1562880613"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Warleader's Helix", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/1/81e474ac-54f7-43f9-8af9-2f1adf258b15.jpg?1562919089", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/1/81e474ac-54f7-43f9-8af9-2f1adf258b15.jpg?1562919089"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Warleader's Helix", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/c/fcc1dd23-90fa-4aa4-b0a9-7a92991ad7ec.jpg?1562640152", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/c/fcc1dd23-90fa-4aa4-b0a9-7a92991ad7ec.jpg?1562640152"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Weight of Spires", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/5/d5f26a87-4562-450c-800b-7d4acc1ae17b.jpg?1593273313", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/5/d5f26a87-4562-450c-800b-7d4acc1ae17b.jpg?1593273313"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Wild Slash", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/9/6975490f-7679-48b3-ba34-04dec97a29c2.jpg?1562826120", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/9/6975490f-7679-48b3-ba34-04dec97a29c2.jpg?1562826120"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Winter Sky", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/f/af1035f3-3027-4a41-834c-55222b13c2bc.jpg?1562588224", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/f/af1035f3-3027-4a41-834c-55222b13c2bc.jpg?1562588224"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Wizard's Lightning", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/9/59bf371a-164c-4db8-9207-197c2e7c3c10.jpg?1562736134", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/9/59bf371a-164c-4db8-9207-197c2e7c3c10.jpg?1562736134"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Word of Blasting", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/5/c5362ead-9162-4160-bfa9-432f7d0e222d.jpg?1562383027", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/5/c5362ead-9162-4160-bfa9-432f7d0e222d.jpg?1562383027"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Word of Blasting", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/6/46b383c8-d604-4131-a869-9e9d13e30b94.jpg?1562907917", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/6/46b383c8-d604-4131-a869-9e9d13e30b94.jpg?1562907917"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Yamabushi's Flame", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/a/1a9bacba-55c4-4b92-bdd9-01b6035ed1b2.jpg?1562757952", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/a/1a9bacba-55c4-4b92-bdd9-01b6035ed1b2.jpg?1562757952"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Zap", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/5/7502ce01-b762-40fe-a064-c7b20b08a722.jpg?1562918451", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/5/7502ce01-b762-40fe-a064-c7b20b08a722.jpg?1562918451"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Zenith Flare", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/e/0efac1ed-3f01-487c-86be-8239568b4425.jpg?1591228324", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/e/0efac1ed-3f01-487c-86be-8239568b4425.jpg?1591228324"}, "reprint": false, "digital": false, "set_type": "expansion"}]}
\ No newline at end of file
diff --git a/web/public/mtg/jsons/counterspell1.json b/web/public/mtg/jsons/counterspell1.json
new file mode 100644
index 00000000..abee75b2
--- /dev/null
+++ b/web/public/mtg/jsons/counterspell1.json
@@ -0,0 +1 @@
+{"has_more": true, "data": [{"name": "Abjure", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/b/fbad9449-d09c-4fd0-b2ad-2aa3a29e03bf.jpg?1562804357", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/b/fbad9449-d09c-4fd0-b2ad-2aa3a29e03bf.jpg?1562804357"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Absorb", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/e/1e8a43c1-42d1-45ef-8a63-4b87775a6e88.jpg?1584831352", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/e/1e8a43c1-42d1-45ef-8a63-4b87775a6e88.jpg?1584831352"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Absorb", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/d/5d6a0f3e-457f-41f5-be26-5fb249874f1a.jpg?1562913952", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/d/5d6a0f3e-457f-41f5-be26-5fb249874f1a.jpg?1562913952"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Absorb Energy", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/f/bfdca67d-9a97-4ddc-8d50-26a48ad2e4b7.jpg?1645416627", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/f/bfdca67d-9a97-4ddc-8d50-26a48ad2e4b7.jpg?1645416627"}, "reprint": false, "digital": true, "set_type": "alchemy"}, {"name": "Abstruse Interference", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/4/249a7be3-311e-4ce6-97dc-97242463ae23.jpg?1562902357", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/4/249a7be3-311e-4ce6-97dc-97242463ae23.jpg?1562902357"}, "reprint": false, "frame_effects": ["devoid"], "digital": false, "set_type": "expansion"}, {"name": "Access Denied", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/6/1642df77-6fe8-47cf-b750-ca4dd9b331ba.jpg?1651655225", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/6/1642df77-6fe8-47cf-b750-ca4dd9b331ba.jpg?1651655225"}, "reprint": false, "digital": false, "set_type": "commander"}, {"name": "Admiral's Order", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/0/80dc0310-afd9-49b4-b58f-a0e91120c38c.jpg?1555039852", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/0/80dc0310-afd9-49b4-b58f-a0e91120c38c.jpg?1555039852"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Aether Gust", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/8/783da808-6698-4e55-9fac-430a6effe2b1.jpg?1592516251", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/8/783da808-6698-4e55-9fac-430a6effe2b1.jpg?1592516251"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Aether Gust", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/c/bcc1aa91-ec97-4fe8-b4b1-a213f050f956.jpg?1645141636", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/c/bcc1aa91-ec97-4fe8-b4b1-a213f050f956.jpg?1645141636"}, "reprint": true, "digital": false, "set_type": "box"}, {"name": "Annul", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/b/4b1d4a59-11a0-4a55-8ac0-07377a9e6dc8.jpg?1631046631", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/b/4b1d4a59-11a0-4a55-8ac0-07377a9e6dc8.jpg?1631046631"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Annul", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/e/5e71b6ad-4b81-4277-8512-0a3f2266cd23.jpg?1562818788", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/e/5e71b6ad-4b81-4277-8512-0a3f2266cd23.jpg?1562818788"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Annul", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/b/8ba18ec8-e82f-41be-9ed8-b1a4ae9b7426.jpg?1562150464", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/b/8ba18ec8-e82f-41be-9ed8-b1a4ae9b7426.jpg?1562150464"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Annul", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/f/3f8c73ff-be92-41ca-93a7-76f9823adb38.jpg?1562908208", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/f/3f8c73ff-be92-41ca-93a7-76f9823adb38.jpg?1562908208"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "An Offer You Can't Refuse", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/9/b9d349f3-5be2-4b1f-a4c3-ba94822cf0cf.jpg?1649394290", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/9/b9d349f3-5be2-4b1f-a4c3-ba94822cf0cf.jpg?1649394290"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Anticognition", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/b/db99b872-77c7-4471-9c44-a36d4ff5d33f.jpg?1604193539", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/b/db99b872-77c7-4471-9c44-a36d4ff5d33f.jpg?1604193539"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Arcane Denial", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/d/9d1ffeb1-6c31-45f7-8140-913c397022a3.jpg?1562439019", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/d/9d1ffeb1-6c31-45f7-8140-913c397022a3.jpg?1562439019"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Arcane Denial", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/b/ab175817-da6a-4ae7-a016-c3bfb087eae0.jpg?1562931100", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/b/ab175817-da6a-4ae7-a016-c3bfb087eae0.jpg?1562931100"}, "reprint": true, "digital": false, "set_type": "commander"}, {"name": "Arcane Denial", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/0/b0c5728e-43e7-417a-ba18-5038345cec67.jpg?1562770144", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/0/b0c5728e-43e7-417a-ba18-5038345cec67.jpg?1562770144"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Arcane Denial", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/1/415a3104-90e6-4235-b67f-69337c7fe714.jpg?1562768258", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/1/415a3104-90e6-4235-b67f-69337c7fe714.jpg?1562768258"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Archmage's Charm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/7/57b852b6-4388-4a41-a5c0-bba37a5c1451.jpg?1562201300", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/7/57b852b6-4388-4a41-a5c0-bba37a5c1451.jpg?1562201300"}, "reprint": false, "digital": false, "set_type": "draft_innovation"}, {"name": "Archmage's Charm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/3/d378f4f8-ff9f-4389-86c8-23c5c4990b4c.jpg?1657849868", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/3/d378f4f8-ff9f-4389-86c8-23c5c4990b4c.jpg?1657849868"}, "reprint": true, "frame_effects": ["inverted"], "digital": false, "set_type": "promo"}, {"name": "Artifact Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/5/1506d99d-7b2e-4101-84a5-c950dadb263a.jpg?1562899411", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/5/1506d99d-7b2e-4101-84a5-c950dadb263a.jpg?1562899411"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Assert Authority", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/c/fc339ed7-e1d4-4fe9-a4c4-b030d3e74c00.jpg?1562163986", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/c/fc339ed7-e1d4-4fe9-a4c4-b030d3e74c00.jpg?1562163986"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Avoid Fate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/2/92f1509e-6ed5-4009-a031-ea84b43cbd1b.jpg?1562859699", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/2/92f1509e-6ed5-4009-a031-ea84b43cbd1b.jpg?1562859699"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Bane's Contingency", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/9/19f81099-f657-4f7d-84ad-f472ae87d9c5.jpg?1653844052", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/9/19f81099-f657-4f7d-84ad-f472ae87d9c5.jpg?1653844052"}, "reprint": false, "digital": false, "set_type": "draft_innovation"}, {"name": "Bant Charm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/5/65b65c87-b084-44aa-b841-411a3c73e234.jpg?1562704776", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/5/65b65c87-b084-44aa-b841-411a3c73e234.jpg?1562704776"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Bar the Gate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/9/b9b1e53f-1384-4860-9944-e68922afc65c.jpg?1627702860", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/9/b9b1e53f-1384-4860-9944-e68922afc65c.jpg?1627702860"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Bind", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/f/cfa51783-9ef8-4e51-ba0d-ce8439d83bdf.jpg?1562936749", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/f/cfa51783-9ef8-4e51-ba0d-ce8439d83bdf.jpg?1562936749"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Bind to Secrecy", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/a/bab838e0-cfc5-4eeb-920d-bfbe462a1e31.jpg?1655963915", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/a/bab838e0-cfc5-4eeb-920d-bfbe462a1e31.jpg?1655963915"}, "reprint": false, "digital": true, "set_type": "alchemy"}, {"name": "Blue Elemental Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/f/2f51f88f-f662-4572-a371-9a77718ed079.jpg?1562434032", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/f/2f51f88f-f662-4572-a371-9a77718ed079.jpg?1562434032"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Blue Elemental Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/0/20d666ef-39bf-4fbf-8201-5f1056539da2.jpg?1559591462", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/0/20d666ef-39bf-4fbf-8201-5f1056539da2.jpg?1559591462"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Blue Elemental Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/5/6582b980-3e4b-422a-9a6c-1927ae966d7e.jpg?1561757308", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/5/6582b980-3e4b-422a-9a6c-1927ae966d7e.jpg?1561757308"}, "reprint": true, "digital": false, "set_type": "spellbook"}, {"name": "Blue Elemental Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/6/a671237a-f895-4bbc-b6bd-b0eed4502ec5.jpg?1562547160", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/6/a671237a-f895-4bbc-b6bd-b0eed4502ec5.jpg?1562547160"}, "reprint": true, "digital": true, "set_type": "promo"}, {"name": "Bone to Ash", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/4/c4a75cef-9551-45e2-b1ff-80662c76ec20.jpg?1562941461", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/4/c4a75cef-9551-45e2-b1ff-80662c76ec20.jpg?1562941461"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Broken Ambitions", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/0/8052d90b-bc49-4a9e-9211-159a54aa2bcd.jpg?1562355294", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/0/8052d90b-bc49-4a9e-9211-159a54aa2bcd.jpg?1562355294"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Broken Concentration", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/5/252eef1f-0a62-420d-aad8-e3d7f1e07c1b.jpg?1576383988", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/5/252eef1f-0a62-420d-aad8-e3d7f1e07c1b.jpg?1576383988"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Brokers Confluence", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/5/657ff5fc-1a95-46f9-85f7-fc1ad757c8c4.jpg?1650506185", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/5/657ff5fc-1a95-46f9-85f7-fc1ad757c8c4.jpg?1650506185"}, "reprint": false, "digital": false, "set_type": "commander"}, {"name": "Brutal Expulsion", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/c/0cd0e11a-0398-431b-b523-9d3c8a0155cb.jpg?1562132495", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/c/0cd0e11a-0398-431b-b523-9d3c8a0155cb.jpg?1562132495"}, "reprint": true, "frame_effects": ["devoid"], "digital": false, "set_type": "promo"}, {"name": "Burnout", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/a/5a8f5a18-e490-4010-ac1c-c74a5f2dcbda.jpg?1562768717", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/a/5a8f5a18-e490-4010-ac1c-c74a5f2dcbda.jpg?1562768717"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Calculated Dismissal", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/c/2c42ab35-6050-42b2-9c3c-3252f2e69442.jpg?1562012331", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/c/2c42ab35-6050-42b2-9c3c-3252f2e69442.jpg?1562012331"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Cancel", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/f/cf6e5ad6-ffe2-4588-b357-c415c33fbc11.jpg?1562564222", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/f/cf6e5ad6-ffe2-4588-b357-c415c33fbc11.jpg?1562564222"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Cancel", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/2/7258e651-868a-4f63-9454-6c6c95d25387.jpg?1543674894", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/2/7258e651-868a-4f63-9454-6c6c95d25387.jpg?1543674894"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Cancel", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/f/9f540dcb-8d0b-4d33-8c0d-893fa5db54eb.jpg?1562791164", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/f/9f540dcb-8d0b-4d33-8c0d-893fa5db54eb.jpg?1562791164"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Cancel", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/d/fd994a26-65ff-43be-8d52-476e887d3ed2.jpg?1562795930", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/d/fd994a26-65ff-43be-8d52-476e887d3ed2.jpg?1562795930"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Cancel", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/e/9e557f54-3d9d-4610-a0d0-5874feacc76e.jpg?1562614848", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/e/9e557f54-3d9d-4610-a0d0-5874feacc76e.jpg?1562614848"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Cancel", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/7/479f56c2-8256-4325-909a-bf460505dbc5.jpg?1562703421", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/7/479f56c2-8256-4325-909a-bf460505dbc5.jpg?1562703421"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Cancel", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/4/b4e175f7-f649-451b-9ee5-ad1140b2e8a7.jpg?1562933181", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/4/b4e175f7-f649-451b-9ee5-ad1140b2e8a7.jpg?1562933181"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Cancel", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/c/bc4d6368-03dc-488a-9a6b-07a549a87572.jpg?1561757939", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/c/bc4d6368-03dc-488a-9a6b-07a549a87572.jpg?1561757939"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Censor", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/c/4cb4e315-1a77-479a-9f15-fb23575de805.jpg?1543674908", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/c/4cb4e315-1a77-479a-9f15-fb23575de805.jpg?1543674908"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Ceremonious Rejection", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/8/08c5ed8e-4804-4042-8a1d-ad24c6846816.jpg?1576381129", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/8/08c5ed8e-4804-4042-8a1d-ad24c6846816.jpg?1576381129"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Circular Logic", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/d/cd9198d6-201d-4175-8f70-eef92d7d5bb5.jpg?1562632085", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/d/cd9198d6-201d-4175-8f70-eef92d7d5bb5.jpg?1562632085"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Clash of Wills", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/6/665ee42f-8d76-4f8b-9dd3-7455a90f0da7.jpg?1562023499", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/6/665ee42f-8d76-4f8b-9dd3-7455a90f0da7.jpg?1562023499"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Clash of Wills", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/c/1c67ab53-9489-4658-859e-9dd8a6e0f20d.jpg?1562636752", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/c/1c67ab53-9489-4658-859e-9dd8a6e0f20d.jpg?1562636752"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Complicate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/3/33f69670-e494-42b8-9148-fe105ec61aa0.jpg?1562907165", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/3/33f69670-e494-42b8-9148-fe105ec61aa0.jpg?1562907165"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Concerted Defense", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/3/235c108d-3902-4c2e-919c-a5449cd2dc3c.jpg?1604193820", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/3/235c108d-3902-4c2e-919c-a5449cd2dc3c.jpg?1604193820"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Condescend", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/b/5ba16c0f-dd42-4a2a-8f08-bc8c8478952b.jpg?1562849378", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/b/5ba16c0f-dd42-4a2a-8f08-bc8c8478952b.jpg?1562849378"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Condescend", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/8/e8303b80-e29a-46b8-90b0-c0cfe551b435.jpg?1562880436", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/8/e8303b80-e29a-46b8-90b0-c0cfe551b435.jpg?1562880436"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Confirm Suspicions", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/f/cf7fcbc2-1034-442d-9f2a-7d79ea40ac3d.jpg?1576384007", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/f/cf7fcbc2-1034-442d-9f2a-7d79ea40ac3d.jpg?1576384007"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Confound", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/f/4f3b7d39-ce98-48e2-b2bf-0d55b4d3102b.jpg?1562911605", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/f/4f3b7d39-ce98-48e2-b2bf-0d55b4d3102b.jpg?1562911605"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Contradict", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/0/a0b3d4ff-09d1-4d9f-8c83-cdfbd7bb1079.jpg?1562790758", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/0/a0b3d4ff-09d1-4d9f-8c83-cdfbd7bb1079.jpg?1562790758"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Controvert", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/e/0e670f6b-d16e-47fc-a5b7-7ca0d8763644.jpg?1593274904", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/e/0e670f6b-d16e-47fc-a5b7-7ca0d8763644.jpg?1593274904"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Convolute", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/f/3fd8e607-8179-4ae8-ba7f-f5f22649dc18.jpg?1591230479", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/f/3fd8e607-8179-4ae8-ba7f-f5f22649dc18.jpg?1591230479"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Convolute", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/1/e17cf756-ec41-4934-8906-4276277c1470.jpg?1576384056", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/1/e17cf756-ec41-4934-8906-4276277c1470.jpg?1576384056"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Convolute", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/a/fac88052-96a3-4a4d-95a2-c5a652fcb275.jpg?1598914075", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/a/fac88052-96a3-4a4d-95a2-c5a652fcb275.jpg?1598914075"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Corrupted Resolve", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/8/28432161-023b-4a98-b92a-55dc6d936cd1.jpg?1562876198", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/8/28432161-023b-4a98-b92a-55dc6d936cd1.jpg?1562876198"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Counterbore", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/4/f4228b80-d87d-4ebe-ae92-04e4a7d0dc43.jpg?1562838120", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/4/f4228b80-d87d-4ebe-ae92-04e4a7d0dc43.jpg?1562838120"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Counterflux", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/4/94e4b773-40a4-4272-85dd-f728ada22748.jpg?1562790128", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/4/94e4b773-40a4-4272-85dd-f728ada22748.jpg?1562790128"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Counterflux", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/8/e864fd80-baee-468e-9dc3-e650cc203b23.jpg?1657120160", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/8/e864fd80-baee-468e-9dc3-e650cc203b23.jpg?1657120160"}, "reprint": true, "digital": false, "set_type": "box"}, {"name": "Counterlash", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/3/d3ec2c57-8e67-472d-8f2e-0492d311f130.jpg?1562945498", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/3/d3ec2c57-8e67-472d-8f2e-0492d311f130.jpg?1562945498"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Countermand", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/7/07815e32-0b64-4c2b-84e6-a72336c45cf5.jpg?1593095401", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/7/07815e32-0b64-4c2b-84e6-a72336c45cf5.jpg?1593095401"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Counterspell", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/c/0c9a7cb0-5bff-48ff-b620-2838816ac9b5.jpg?1580013910", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/c/0c9a7cb0-5bff-48ff-b620-2838816ac9b5.jpg?1580013910"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Counterspell", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/1/71cfcba5-1571-48b8-a3db-55dca135506e.jpg?1562843855", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/1/71cfcba5-1571-48b8-a3db-55dca135506e.jpg?1562843855"}, "reprint": true, "digital": false, "set_type": "duel_deck"}, {"name": "Counterspell", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/9/29bb1b85-9444-4bfa-b622-092a6873631c.jpg?1562234566", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/9/29bb1b85-9444-4bfa-b622-092a6873631c.jpg?1562234566"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Counterspell", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/b/7bd03c80-7812-4704-9e07-9cf73b49c01f.jpg?1562381815", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/b/7bd03c80-7812-4704-9e07-9cf73b49c01f.jpg?1562381815"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Counterspell", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/a/dacdd380-71cf-4832-bd02-3697501325f3.jpg?1562056885", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/a/dacdd380-71cf-4832-bd02-3697501325f3.jpg?1562056885"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Counterspell", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/9/b975289d-d8b8-46b4-8c60-d6ed4b594519.jpg?1562593755", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/9/b975289d-d8b8-46b4-8c60-d6ed4b594519.jpg?1562593755"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Counterspell", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/e/aedbcbaa-40f0-485f-8427-778edc2d2ec0.jpg?1562927522", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/e/aedbcbaa-40f0-485f-8427-778edc2d2ec0.jpg?1562927522"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Counterspell", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/d/0df55e3f-14de-46ef-b6b1-616618724d9e.jpg?1559591713", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/d/0df55e3f-14de-46ef-b6b1-616618724d9e.jpg?1559591713"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Counterspell", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/3/f35ec9da-f38b-4b7f-9eb5-090ca7755668.jpg?1645141660", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/3/f35ec9da-f38b-4b7f-9eb5-090ca7755668.jpg?1645141660"}, "reprint": true, "digital": false, "set_type": "box"}, {"name": "Counterspell", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/c/2c358d75-01ad-4487-8104-425124b96aae.jpg?1628337127", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/c/2c358d75-01ad-4487-8104-425124b96aae.jpg?1628337127"}, "reprint": true, "frame_effects": ["inverted"], "digital": false, "set_type": "draft_innovation"}, {"name": "Counterspell", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/f/ffdf9d2a-c163-43df-9a2f-20b8749c86ae.jpg?1631491044", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/f/ffdf9d2a-c163-43df-9a2f-20b8749c86ae.jpg?1631491044"}, "reprint": true, "digital": false, "set_type": "masterpiece"}, {"name": "Counterspell", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/1/3126d20f-1082-4ebc-b2fa-b12be3ba1bac.jpg?1562904991", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/1/3126d20f-1082-4ebc-b2fa-b12be3ba1bac.jpg?1562904991"}, "reprint": true, "digital": false, "set_type": "masterpiece"}, {"name": "Counterspell", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/0/7065deea-6117-47d4-9d72-fc67af5bb483.jpg?1561757383", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/0/7065deea-6117-47d4-9d72-fc67af5bb483.jpg?1561757383"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Countersquall", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/b/2b645d74-420e-45e5-aa82-ba3a8dfdd9a0.jpg?1562905206", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/b/2b645d74-420e-45e5-aa82-ba3a8dfdd9a0.jpg?1562905206"}, "reprint": true, "digital": false, "set_type": "duel_deck"}, {"name": "Countersquall", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/c/ec16e216-95e1-41f7-87e0-78b6ac3fe1df.jpg?1562804491", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/c/ec16e216-95e1-41f7-87e0-78b6ac3fe1df.jpg?1562804491"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Countervailing Winds", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/e/de1c0ef3-b32c-403a-93cb-29cf05795711.jpg?1562817497", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/e/de1c0ef3-b32c-403a-93cb-29cf05795711.jpg?1562817497"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Crush Dissent", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/4/94c70f23-0ca9-425e-a53a-6c09921c0075.jpg?1557576187", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/4/94c70f23-0ca9-425e-a53a-6c09921c0075.jpg?1557576187"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Cryptic Command", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/0/30f6fca9-003b-4f6b-9d6e-1e88adda4155.jpg?1562847413", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/0/30f6fca9-003b-4f6b-9d6e-1e88adda4155.jpg?1562847413"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Cryptic Command", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/2/829e3d6e-5d7c-4cc4-a7a6-7cbf5a7442ba.jpg?1562355759", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/2/829e3d6e-5d7c-4cc4-a7a6-7cbf5a7442ba.jpg?1562355759"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Cryptic Command", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/2/a2a384c1-a05f-4f00-bd77-f897d9819971.jpg?1562927862", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/2/a2a384c1-a05f-4f00-bd77-f897d9819971.jpg?1562927862"}, "reprint": true, "digital": false, "set_type": "masterpiece"}, {"name": "Cryptic Command", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/2/526607e9-1907-4639-b944-8ee152c81bfb.jpg?1561757137", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/2/526607e9-1907-4639-b944-8ee152c81bfb.jpg?1561757137"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Dash Hopes", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/1/814bcfc0-7539-4ed9-8b51-27e6a3ab9d9a.jpg?1562575740", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/1/814bcfc0-7539-4ed9-8b51-27e6a3ab9d9a.jpg?1562575740"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Dawn Charm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/4/a4c9667b-1d94-42eb-ae8e-1ae4755e200a.jpg?1562578420", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/4/a4c9667b-1d94-42eb-ae8e-1ae4755e200a.jpg?1562578420"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Daze", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/0/f05e9a3e-8a35-4687-85cb-e31b3927a5e2.jpg?1580013916", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/0/f05e9a3e-8a35-4687-85cb-e31b3927a5e2.jpg?1580013916"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Daze", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/0/d03bff25-0d5e-4dcf-8d75-6df846afea3b.jpg?1562632115", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/0/d03bff25-0d5e-4dcf-8d75-6df846afea3b.jpg?1562632115"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Daze", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/9/a9b037f1-3298-4ba8-92a8-0843f6e497d7.jpg?1562929191", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/9/a9b037f1-3298-4ba8-92a8-0843f6e497d7.jpg?1562929191"}, "reprint": true, "digital": false, "set_type": "masterpiece"}, {"name": "Decisive Denial", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/2/b2e9d132-95f7-4ee7-9c91-be19e4ad7a5d.jpg?1627428577", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/2/b2e9d132-95f7-4ee7-9c91-be19e4ad7a5d.jpg?1627428577"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Delay", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/9/3906d538-f1ca-4799-b91c-2e0d2934f241.jpg?1619393997", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/9/3906d538-f1ca-4799-b91c-2e0d2934f241.jpg?1619393997"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Delay", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/8/e821d337-4bc5-4401-ac9b-34adf4012b73.jpg?1562941573", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/8/e821d337-4bc5-4401-ac9b-34adf4012b73.jpg?1562941573"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Denied!", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/2/1285c125-e145-4565-a029-352ac6adf688.jpg?1562799062", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/2/1285c125-e145-4565-a029-352ac6adf688.jpg?1562799062"}, "reprint": false, "digital": false, "set_type": "funny"}, {"name": "Deny Existence", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/6/16a14eeb-1c85-4029-a047-39a4efef3f74.jpg?1576384025", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/6/16a14eeb-1c85-4029-a047-39a4efef3f74.jpg?1576384025"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Deny the Divine", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/2/1200f68a-a8ea-4777-b6b0-de48b2203fd1.jpg?1588900840", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/2/1200f68a-a8ea-4777-b6b0-de48b2203fd1.jpg?1588900840"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Deprive", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/e/2efecdd9-bd3a-4b79-92da-6485589d5bde.jpg?1562702470", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/e/2efecdd9-bd3a-4b79-92da-6485589d5bde.jpg?1562702470"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Desertion", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/a/9a2a1779-af08-4a9a-aba4-e6892ce2332c.jpg?1562278155", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/a/9a2a1779-af08-4a9a-aba4-e6892ce2332c.jpg?1562278155"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Devious Cover-Up", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/4/648281fe-89fb-4d8d-b944-3af28fb044f6.jpg?1634348751", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/4/648281fe-89fb-4d8d-b944-3af28fb044f6.jpg?1634348751"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Devious Cover-Up", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/1/21ac6b0a-b1a5-439d-b65e-5f04e1826c80.jpg?1636491628", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/1/21ac6b0a-b1a5-439d-b65e-5f04e1826c80.jpg?1636491628"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Didn't Say Please", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/7/77500b53-0852-4d6a-bfe3-b1e8ef5a12cd.jpg?1572489858", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/7/77500b53-0852-4d6a-bfe3-b1e8ef5a12cd.jpg?1572489858"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Dimir Charm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/3/f3f4cfa7-8ee4-4a85-9e6a-65a7541f62c1.jpg?1561852231", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/3/f3f4cfa7-8ee4-4a85-9e6a-65a7541f62c1.jpg?1561852231"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Dimir Charm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/f/9f6bc1da-3969-4f19-b072-4ed79f906fef.jpg?1562497257", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/f/9f6bc1da-3969-4f19-b072-4ed79f906fef.jpg?1562497257"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Disallow", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/5/25f05814-a5a5-460f-9d29-0ab03efecf4c.jpg?1576381471", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/5/25f05814-a5a5-460f-9d29-0ab03efecf4c.jpg?1576381471"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Disappearing Act", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/a/9a4a6d56-9bed-444c-aae8-383c315779a0.jpg?1576381158", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/a/9a4a6d56-9bed-444c-aae8-383c315779a0.jpg?1576381158"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Discombobulate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/e/cef584c5-6e2d-419b-9c11-a1b6c9c9ab2a.jpg?1562943839", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/e/cef584c5-6e2d-419b-9c11-a1b6c9c9ab2a.jpg?1562943839"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Discontinuity", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/3/b33ba0a8-04e9-4df6-af20-a3ca4470cdcc.jpg?1594735451", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/3/b33ba0a8-04e9-4df6-af20-a3ca4470cdcc.jpg?1594735451"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Disdainful Stroke", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/9/492aa24c-61c4-48bc-b7b7-f423be2662da.jpg?1649881231", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/9/492aa24c-61c4-48bc-b7b7-f423be2662da.jpg?1649881231"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Disdainful Stroke", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/6/7691ac89-f8ba-493e-aa11-5674a783dffb.jpg?1631047007", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/6/7691ac89-f8ba-493e-aa11-5674a783dffb.jpg?1631047007"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Disdainful Stroke", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/1/0193dfa3-8409-44be-b4be-6c3cad42d4a4.jpg?1572892724", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/1/0193dfa3-8409-44be-b4be-6c3cad42d4a4.jpg?1572892724"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Disdainful Stroke", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/8/180425c9-1898-48d4-9932-ddfb1a28e6b0.jpg?1562783110", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/8/180425c9-1898-48d4-9932-ddfb1a28e6b0.jpg?1562783110"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Disdainful Stroke", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/7/3711f61d-6381-4c92-a3f5-6deed29aae47.jpg?1562639749", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/7/3711f61d-6381-4c92-a3f5-6deed29aae47.jpg?1562639749"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Dismal Failure", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/5/35786a7a-faa6-457d-9b92-da560b93a43a.jpg?1562569290", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/5/35786a7a-faa6-457d-9b92-da560b93a43a.jpg?1562569290"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Dismiss", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/e/1e55d6be-7682-4786-9872-e847afd710b0.jpg?1562052798", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/e/1e55d6be-7682-4786-9872-e847afd710b0.jpg?1562052798"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Dispel", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/c/bceab6b3-6b64-4964-a501-ce806a6c13ad.jpg?1562939587", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/c/bceab6b3-6b64-4964-a501-ce806a6c13ad.jpg?1562939587"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Dispel", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/8/08d4a8d7-c136-472f-8146-a1100701ca4f.jpg?1562782227", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/8/08d4a8d7-c136-472f-8146-a1100701ca4f.jpg?1562782227"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Dispel", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/1/f178d0cc-5dd1-41ab-a2e8-218ece6f2a86.jpg?1562299138", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/1/f178d0cc-5dd1-41ab-a2e8-218ece6f2a86.jpg?1562299138"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Dispersal Shield", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/c/0c257df6-f275-40db-bfe3-a9291356cdf7.jpg?1562525399", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/c/0c257df6-f275-40db-bfe3-a9291356cdf7.jpg?1562525399"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Disrupt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/0/c000a02f-6b7e-4925-a938-59e645e980d7.jpg?1562933600", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/0/c000a02f-6b7e-4925-a938-59e645e980d7.jpg?1562933600"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Disrupt", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/6/c6cc89b0-9acf-452b-ac1a-bc7e90eb32fc.jpg?1562803281", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/6/c6cc89b0-9acf-452b-ac1a-bc7e90eb32fc.jpg?1562803281"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Disrupting Shoal", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/5/15589745-4c0a-4edf-ad45-3b7fa45e70c5.jpg?1562875608", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/5/15589745-4c0a-4edf-ad45-3b7fa45e70c5.jpg?1562875608"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Disruption Protocol", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/5/053ab598-06a4-43ae-b9fd-c291bd05642c.jpg?1654566666", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/5/053ab598-06a4-43ae-b9fd-c291bd05642c.jpg?1654566666"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Dissipate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/6/4689b3f2-e4b7-448e-b3d4-ab33194aafb2.jpg?1634348774", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/6/4689b3f2-e4b7-448e-b3d4-ab33194aafb2.jpg?1634348774"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Dissipate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/d/5d778082-bcdb-423a-b16f-57ac0d4dace7.jpg?1562830916", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/d/5d778082-bcdb-423a-b16f-57ac0d4dace7.jpg?1562830916"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Dissipate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/6/36d9271d-6dbf-4640-9222-721a7a3ccc08.jpg?1562718782", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/6/36d9271d-6dbf-4640-9222-721a7a3ccc08.jpg?1562718782"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Dissolve", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/9/992e8119-f933-4e54-bb04-e1cc78f7e87b.jpg?1562821811", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/9/992e8119-f933-4e54-bb04-e1cc78f7e87b.jpg?1562821811"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Dissolve", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/2/f2068083-5d53-43c3-af22-79bf617ccf1b.jpg?1562640127", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/2/f2068083-5d53-43c3-af22-79bf617ccf1b.jpg?1562640127"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Divide by Zero", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/9/1958d96e-ec44-48ab-80b1-5b01a24ac7b8.jpg?1644607565", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/9/1958d96e-ec44-48ab-80b1-5b01a24ac7b8.jpg?1644607565"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Double Negative", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/c/2c7e3c58-3cda-4891-8b3d-33bb21568cf5.jpg?1562640325", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/c/2c7e3c58-3cda-4891-8b3d-33bb21568cf5.jpg?1562640325"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Dovin's Veto", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/6/d60ca98f-7f91-4bbd-9d06-dadb0c1da282.jpg?1570573658", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/6/d60ca98f-7f91-4bbd-9d06-dadb0c1da282.jpg?1570573658"}, "reprint": true, "frame_effects": ["inverted"], "digital": false, "set_type": "promo"}, {"name": "Dream Fracture", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/a/daca6a57-38b7-4547-9174-a7f548ea1258.jpg?1653691053", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/a/daca6a57-38b7-4547-9174-a7f548ea1258.jpg?1653691053"}, "reprint": true, "digital": false, "set_type": "draft_innovation"}, {"name": "Dream Fracture", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/c/4cfd71ff-d899-4f5b-b7df-ec47e2840be9.jpg?1562911180", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/c/4cfd71ff-d899-4f5b-b7df-ec47e2840be9.jpg?1562911180"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Dromar's Charm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/9/69f752d3-3f42-4275-be09-d257c89da70d.jpg?1562917160", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/9/69f752d3-3f42-4275-be09-d257c89da70d.jpg?1562917160"}, "reprint": true, "digital": false, "set_type": "commander"}, {"name": "Dromar's Charm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/7/c7a1894c-af4e-4530-960f-2225916be8cb.jpg?1562937176", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/7/c7a1894c-af4e-4530-960f-2225916be8cb.jpg?1562937176"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Drown in the Loch", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/b/8bf5df5b-164d-4ec2-a5e6-bbaea152e271.jpg?1572490739", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/b/8bf5df5b-164d-4ec2-a5e6-bbaea152e271.jpg?1572490739"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Drown in the Loch", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/1/01acd1c1-86b2-4423-9ba7-5b9725c0514f.jpg?1640249448", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/1/01acd1c1-86b2-4423-9ba7-5b9725c0514f.jpg?1640249448"}, "reprint": true, "digital": false, "set_type": "box"}, {"name": "Endless Detour", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/3/13798c8c-1aa5-4f95-979b-b971e73d715f.jpg?1649942599", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/3/13798c8c-1aa5-4f95-979b-b971e73d715f.jpg?1649942599"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Endless Detour", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/5/e55503d2-1b32-43cf-95c6-a4a61047a4dc.jpg?1649942620", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/5/e55503d2-1b32-43cf-95c6-a4a61047a4dc.jpg?1649942620"}, "reprint": false, "frame_effects": ["showcase"], "digital": false, "set_type": "expansion"}, {"name": "Envelop", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/7/e7ed250e-12d0-4ebc-9410-5711e71c6d1f.jpg?1562632433", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/7/e7ed250e-12d0-4ebc-9410-5711e71c6d1f.jpg?1562632433"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Ertai's Meddling", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/5/35c7e7fa-1493-4ef8-9cdb-b02b07a1ad85.jpg?1562053736", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/5/35c7e7fa-1493-4ef8-9cdb-b02b07a1ad85.jpg?1562053736"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Ertai's Trickery", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/4/544e3575-9fb6-41f7-a4e6-f8460dfae344.jpg?1562912607", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/4/544e3575-9fb6-41f7-a4e6-f8460dfae344.jpg?1562912607"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Essence Backlash", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/9/a98609dc-ea90-4c7e-a191-5e5d0ba16847.jpg?1562791298", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/9/a98609dc-ea90-4c7e-a191-5e5d0ba16847.jpg?1562791298"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Essence Capture", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/3/f39bf1fa-b530-4353-a683-843466227109.jpg?1654566672", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/3/f39bf1fa-b530-4353-a683-843466227109.jpg?1654566672"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Essence Capture", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/e/ce137910-0f0e-4f94-9b95-6e0eeeba164e.jpg?1584830187", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/e/ce137910-0f0e-4f94-9b95-6e0eeeba164e.jpg?1584830187"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Essence Scatter", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/f/5f79c8a0-291e-4e13-b765-4cf8c726cf30.jpg?1636491405", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/f/5f79c8a0-291e-4e13-b765-4cf8c726cf30.jpg?1636491405"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Essence Scatter", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/1/e1e325e1-f1f9-4448-84e3-1fd929b0bc12.jpg?1543674950", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/1/e1e325e1-f1f9-4448-84e3-1fd929b0bc12.jpg?1543674950"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Essence Scatter", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/2/c231101e-6620-46fc-a0ad-a53291d12dc2.jpg?1561994248", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/2/c231101e-6620-46fc-a0ad-a53291d12dc2.jpg?1561994248"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Evasive Action", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/d/5d0b4f29-ada4-41d2-8292-b5af537c6fd2.jpg?1562916923", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/d/5d0b4f29-ada4-41d2-8292-b5af537c6fd2.jpg?1562916923"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Exclude", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/1/a1a50f54-6363-41dd-88a7-9f9e820e7d5f.jpg?1562439432", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/1/a1a50f54-6363-41dd-88a7-9f9e820e7d5f.jpg?1562439432"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Exclude", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/7/970864e0-5488-4b6f-9316-3e3b4098770e.jpg?1561951119", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/7/970864e0-5488-4b6f-9316-3e3b4098770e.jpg?1561951119"}, "reprint": true, "digital": false, "set_type": "commander"}, {"name": "Exclude", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/e/aeb359c8-209c-455f-84b2-970e5678a9fa.jpg?1562930137", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/e/aeb359c8-209c-455f-84b2-970e5678a9fa.jpg?1562930137"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Extinguish", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/1/21140417-09f5-4d05-b94c-355fde9b4719.jpg?1562255853", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/1/21140417-09f5-4d05-b94c-355fde9b4719.jpg?1562255853"}, "reprint": true, "digital": false, "set_type": "starter"}, {"name": "Extinguish", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/4/641f4e66-b46b-4da3-a053-f3763400d4f5.jpg?1562918557", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/4/641f4e66-b46b-4da3-a053-f3763400d4f5.jpg?1562918557"}, "reprint": false, "digital": false, "set_type": "starter"}, {"name": "Faerie Trickery", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/e/defb9f0b-195e-4aeb-92c1-8f827ad6724b.jpg?1562371108", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/e/defb9f0b-195e-4aeb-92c1-8f827ad6724b.jpg?1562371108"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Failed Inspection", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/8/f8900f91-cb17-4f99-a5ce-15819369beb8.jpg?1576381199", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/8/f8900f91-cb17-4f99-a5ce-15819369beb8.jpg?1576381199"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Fall of the Gavel", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/4/64f42848-963b-4b16-aeec-66d0f349758b.jpg?1562787318", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/4/64f42848-963b-4b16-aeec-66d0f349758b.jpg?1562787318"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "False Summoning", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/d/cd7d30a8-bc7a-42bc-8d1b-600cbf78ab98.jpg?1562943500", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/d/cd7d30a8-bc7a-42bc-8d1b-600cbf78ab98.jpg?1562943500"}, "reprint": false, "digital": false, "set_type": "starter"}, {"name": "Familiar's Ruse", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/5/55b9be91-f3a1-49ce-8a3e-2ecd30e2e692.jpg?1562348978", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/5/55b9be91-f3a1-49ce-8a3e-2ecd30e2e692.jpg?1562348978"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Fervent Denial", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/b/7b15428e-946e-490d-93bb-9888bfd3a1df.jpg?1568003997", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/b/7b15428e-946e-490d-93bb-9888bfd3a1df.jpg?1568003997"}, "reprint": true, "digital": false, "set_type": "commander"}, {"name": "Fervent Denial", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/d/ed13fdb4-f28a-43c9-a69f-bab227806c39.jpg?1562939482", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/d/ed13fdb4-f28a-43c9-a69f-bab227806c39.jpg?1562939482"}, "reprint": false, "frame_effects": ["tombstone"], "digital": false, "set_type": "expansion"}, {"name": "Fierce Guardianship", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/c/4c5ffa83-c88d-4f5d-851e-a642b229d596.jpg?1591319453", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/c/4c5ffa83-c88d-4f5d-851e-a642b229d596.jpg?1591319453"}, "reprint": false, "digital": false, "set_type": "commander"}, {"name": "Flaccify", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/0/409cb48a-572a-40df-ae1a-a43feab6bdfd.jpg?1562487932", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/0/409cb48a-572a-40df-ae1a-a43feab6bdfd.jpg?1562487932"}, "reprint": false, "digital": false, "set_type": "funny"}, {"name": "Flash Counter", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/c/dc14e61f-481a-4bfa-aca0-fb63dc952be6.jpg?1562939250", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/c/dc14e61f-481a-4bfa-aca0-fb63dc952be6.jpg?1562939250"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Flash Counter", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/c/3c3cd450-f1cd-416b-9271-37d95815c089.jpg?1587858200", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/c/3c3cd450-f1cd-416b-9271-37d95815c089.jpg?1587858200"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Flashfreeze", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/e/cefd9955-a195-4855-a00e-3809b96ca92b.jpg?1593274923", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/e/cefd9955-a195-4855-a00e-3809b96ca92b.jpg?1593274923"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Flip the Switch", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/c/5cdbe4e3-f030-46fa-ae84-edf261b61706.jpg?1634348893", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/c/5cdbe4e3-f030-46fa-ae84-edf261b61706.jpg?1634348893"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Flusterstorm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/e/1e2e09bf-e7c8-4f13-bcee-f9c8cbc57993.jpg?1592713006", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/e/1e2e09bf-e7c8-4f13-bcee-f9c8cbc57993.jpg?1592713006"}, "reprint": false, "digital": false, "set_type": "commander"}, {"name": "Flusterstorm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/c/9c2077c2-81ce-4ddf-82f0-6fece362d6d7.jpg?1562546827", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/c/9c2077c2-81ce-4ddf-82f0-6fece362d6d7.jpg?1562546827"}, "reprint": true, "digital": true, "set_type": "promo"}, {"name": "Foil", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/8/e8b39fd6-9240-4f76-b12c-e7d9aa88f061.jpg?1547516254", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/8/e8b39fd6-9240-4f76-b12c-e7d9aa88f061.jpg?1547516254"}, "reprint": true, "digital": false, "set_type": "masters"}]}
\ No newline at end of file
diff --git a/web/public/mtg/jsons/counterspell2.json b/web/public/mtg/jsons/counterspell2.json
new file mode 100644
index 00000000..e32ed2c7
--- /dev/null
+++ b/web/public/mtg/jsons/counterspell2.json
@@ -0,0 +1 @@
+{"has_more": true, "data": [{"name": "Foil", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/7/870fb793-3107-4cb2-ba78-34fbf5c9da2f.jpg?1562920018", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/7/870fb793-3107-4cb2-ba78-34fbf5c9da2f.jpg?1562920018"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Fold into Aether", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/1/615157d6-0160-417b-b06c-0e253b306c37.jpg?1562877336", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/1/615157d6-0160-417b-b06c-0e253b306c37.jpg?1562877336"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Forbid", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/9/29df5ef7-d679-4543-bdb7-3984155c87e0.jpg?1562087370", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/9/29df5ef7-d679-4543-bdb7-3984155c87e0.jpg?1562087370"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Forbid", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/4/14a9cc52-a45b-4cde-8aff-d672b35c3118.jpg?1562899128", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/4/14a9cc52-a45b-4cde-8aff-d672b35c3118.jpg?1562899128"}, "reprint": true, "digital": false, "set_type": "masterpiece"}, {"name": "Forceful Denial", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/7/27c75157-2670-4804-8853-a6867c83c40a.jpg?1608909212", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/7/27c75157-2670-4804-8853-a6867c83c40a.jpg?1608909212"}, "reprint": false, "digital": false, "set_type": "draft_innovation"}, {"name": "Force of Negation", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/9/e9be371c-c688-44ad-ab71-bd4c9f242d58.jpg?1562201382", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/9/e9be371c-c688-44ad-ab71-bd4c9f242d58.jpg?1562201382"}, "reprint": false, "digital": false, "set_type": "draft_innovation"}, {"name": "Force of Negation", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/3/5396b405-6fa0-43d7-a8f6-f64154e95e98.jpg?1655823932", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/3/5396b405-6fa0-43d7-a8f6-f64154e95e98.jpg?1655823932"}, "reprint": true, "frame_effects": ["inverted"], "digital": false, "set_type": "masters"}, {"name": "Force of Will", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/b/ebc01ab4-d89a-4d25-bf54-6aed33772f4b.jpg?1580013954", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/b/ebc01ab4-d89a-4d25-bf54-6aed33772f4b.jpg?1580013954"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Force of Will", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/a/9a879b60-4381-447d-8a5a-8e0b6a1d49ca.jpg?1562769672", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/a/9a879b60-4381-447d-8a5a-8e0b6a1d49ca.jpg?1562769672"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Force of Will", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/c/ec136ce7-bad4-4ebb-ab00-b86de3d209a7.jpg?1599710933", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/c/ec136ce7-bad4-4ebb-ab00-b86de3d209a7.jpg?1599710933"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Force of Will", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/2/026983a4-03ca-4812-b129-5ea523596942.jpg?1562895460", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/2/026983a4-03ca-4812-b129-5ea523596942.jpg?1562895460"}, "reprint": true, "digital": false, "set_type": "masterpiece"}, {"name": "Force of Will", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/3/53ed5673-728f-4da3-ad18-3bd72032e815.jpg?1562258455", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/3/53ed5673-728f-4da3-ad18-3bd72032e815.jpg?1562258455"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Force Spike", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/d/1d03d73f-0530-4125-8689-1c43e502e331.jpg?1562233829", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/d/1d03d73f-0530-4125-8689-1c43e502e331.jpg?1562233829"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Force Spike", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/a/ba23d540-8c2d-4a42-b4c0-86f0988bd1ce.jpg?1562593757", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/a/ba23d540-8c2d-4a42-b4c0-86f0988bd1ce.jpg?1562593757"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Force Spike", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/0/70e64028-ae96-4950-aa6c-9d347409fad3.jpg?1562859654", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/0/70e64028-ae96-4950-aa6c-9d347409fad3.jpg?1562859654"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Force Void", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/2/226555ba-22af-45f1-a3f4-d265f8685dd5.jpg?1587911634", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/2/226555ba-22af-45f1-a3f4-d265f8685dd5.jpg?1587911634"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Frazzle", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/8/68b7f705-4d64-4551-8d76-826d91324e9e.jpg?1593271993", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/8/68b7f705-4d64-4551-8d76-826d91324e9e.jpg?1593271993"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Frightful Delusion", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/8/38c9ba98-90b4-4c28-9eef-a4fe0913b921.jpg?1562828708", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/8/38c9ba98-90b4-4c28-9eef-a4fe0913b921.jpg?1562828708"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Fuel for the Cause", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/1/4126e0e5-9b23-496f-8a09-7a35499f9a09.jpg?1562610827", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/1/4126e0e5-9b23-496f-8a09-7a35499f9a09.jpg?1562610827"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Gainsay", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/6/e658939a-fa5a-4497-b35c-b6fbfa3f6882.jpg?1562835545", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/6/e658939a-fa5a-4497-b35c-b6fbfa3f6882.jpg?1562835545"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Gainsay", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/7/a70a2092-5048-49c0-9351-a3f882c2f56e.jpg?1562930170", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/7/a70a2092-5048-49c0-9351-a3f882c2f56e.jpg?1562930170"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Gale's Redirection", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/f/1f5ddcf8-c87b-4a26-b226-8593f517a74a.jpg?1653353395", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/f/1f5ddcf8-c87b-4a26-b226-8593f517a74a.jpg?1653353395"}, "reprint": false, "digital": false, "set_type": "draft_innovation"}, {"name": "Geistlight Snare", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/3/7302b5da-cac5-4ce7-ad38-2ff4e410891b.jpg?1643587841", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/3/7302b5da-cac5-4ce7-ad38-2ff4e410891b.jpg?1643587841"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Geist Snatch", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/6/b6dac5db-ef96-4bd5-aabc-e5ae2b95c8c3.jpg?1592708554", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/6/b6dac5db-ef96-4bd5-aabc-e5ae2b95c8c3.jpg?1592708554"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Glorious End", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/9/2922b976-7beb-4c68-b39e-1b66d5c6f65e.jpg?1543675588", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/9/2922b976-7beb-4c68-b39e-1b66d5c6f65e.jpg?1543675588"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Grip of Amnesia", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/3/43dc7e2a-5b9b-4f0f-8b2e-a7c7f847e1f1.jpg?1562629609", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/3/43dc7e2a-5b9b-4f0f-8b2e-a7c7f847e1f1.jpg?1562629609"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Guttural Response", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/1/9121e55e-5070-48cc-b706-92c67ad89254.jpg?1592761849", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/1/9121e55e-5070-48cc-b706-92c67ad89254.jpg?1592761849"}, "reprint": true, "digital": false, "set_type": "duel_deck"}, {"name": "Guttural Response", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/0/e0662ab6-b475-4b8d-ae77-a9b654e611da.jpg?1562837134", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/0/e0662ab6-b475-4b8d-ae77-a9b654e611da.jpg?1562837134"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Halt Order", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/f/7fed18af-7301-4d03-ba7c-e94f07f078b3.jpg?1562819574", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/f/7fed18af-7301-4d03-ba7c-e94f07f078b3.jpg?1562819574"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Hinder", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/c/dc7befed-805b-4a02-a87d-7df3a95db8a0.jpg?1562765119", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/c/dc7befed-805b-4a02-a87d-7df3a95db8a0.jpg?1562765119"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Hinder", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/7/679d6226-7ec1-44f3-ac90-30b123501aa0.jpg?1561757329", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/7/679d6226-7ec1-44f3-ac90-30b123501aa0.jpg?1561757329"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Hindering Light", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/8/98e43870-4bed-4d76-a633-a6326c736d22.jpg?1562706936", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/8/98e43870-4bed-4d76-a633-a6326c736d22.jpg?1562706936"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Hindering Touch", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/b/db9735d9-4aac-4175-8ec8-fc9bfd8f2c5c.jpg?1562535667", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/b/db9735d9-4aac-4175-8ec8-fc9bfd8f2c5c.jpg?1562535667"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Hisoka's Defiance", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/9/09fd4d01-1204-46a3-b237-45c37985acac.jpg?1562757466", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/9/09fd4d01-1204-46a3-b237-45c37985acac.jpg?1562757466"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Hornswoggle", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/1/b10b8f15-b323-44d8-85a7-ed662a40889d.jpg?1555039907", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/1/b10b8f15-b323-44d8-85a7-ed662a40889d.jpg?1555039907"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Horribly Awry", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/c/4cd05532-686e-40dc-858b-8a77a3628c99.jpg?1562912968", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/c/4cd05532-686e-40dc-858b-8a77a3628c99.jpg?1562912968"}, "reprint": false, "frame_effects": ["devoid"], "digital": false, "set_type": "expansion"}, {"name": "Hydroblast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/c/4c9c9b16-5567-4473-95e6-622292f77336.jpg?1580013995", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/c/4c9c9b16-5567-4473-95e6-622292f77336.jpg?1580013995"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Hydroblast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/6/f62716f0-fde2-49ef-b8a4-c1b03f451194.jpg?1562941220", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/6/f62716f0-fde2-49ef-b8a4-c1b03f451194.jpg?1562941220"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Hydroblast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/2/222db3a6-c2b1-48fc-9b0c-018ac6ed517b.jpg?1562543501", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/2/222db3a6-c2b1-48fc-9b0c-018ac6ed517b.jpg?1562543501"}, "reprint": true, "digital": true, "set_type": "promo"}, {"name": "Illumination", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/b/eb28f6e5-c9ef-416e-b315-967d857e7600.jpg?1562722393", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/b/eb28f6e5-c9ef-416e-b315-967d857e7600.jpg?1562722393"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Induce Paranoia", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/c/bc462b75-8b08-47a3-be22-d7b5c062ec5b.jpg?1598914307", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/c/bc462b75-8b08-47a3-be22-d7b5c062ec5b.jpg?1598914307"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Insidious Will", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/e/8eafb2bb-58bf-4c6b-ae8f-91bcea12c7d2.jpg?1576381260", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/e/8eafb2bb-58bf-4c6b-ae8f-91bcea12c7d2.jpg?1576381260"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Interdict", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/4/3442c919-73b9-4d29-a014-87293f456325.jpg?1562053290", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/4/3442c919-73b9-4d29-a014-87293f456325.jpg?1562053290"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Intervene", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/b/4b0e3894-5dfe-4d03-9996-eebf96c58168.jpg?1562862808", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/b/4b0e3894-5dfe-4d03-9996-eebf96c58168.jpg?1562862808"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Invasive Surgery", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/e/6e644e38-39bf-40bd-9be1-5eb80f472e81.jpg?1576384110", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/e/6e644e38-39bf-40bd-9be1-5eb80f472e81.jpg?1576384110"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Ionize", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/1/f161f7d2-eaa1-4931-93f9-befa8b5df821.jpg?1572893679", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/1/f161f7d2-eaa1-4931-93f9-befa8b5df821.jpg?1572893679"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Ixidor's Will", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/b/1b713448-853a-41ee-a302-963e9c1c1c65.jpg?1562901464", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/b/1b713448-853a-41ee-a302-963e9c1c1c65.jpg?1562901464"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Izzet Charm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/1/61289196-a56b-4d24-b340-9cf067c77f45.jpg?1592713417", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/1/61289196-a56b-4d24-b340-9cf067c77f45.jpg?1592713417"}, "reprint": true, "digital": false, "set_type": "duel_deck"}, {"name": "Izzet Charm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/8/e8e84a97-8e40-42fa-a114-df90e820ede6.jpg?1562497263", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/8/e8e84a97-8e40-42fa-a114-df90e820ede6.jpg?1562497263"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Jace's Defeat", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/6/c6b103c1-9b25-4bfe-9081-570977e9fdad.jpg?1562814148", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/6/c6b103c1-9b25-4bfe-9081-570977e9fdad.jpg?1562814148"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Jaded Response", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/a/6a9ab1f0-4e75-4165-85bc-6f838c221d6a.jpg?1562920093", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/a/6a9ab1f0-4e75-4165-85bc-6f838c221d6a.jpg?1562920093"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Keep Safe", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/e/febfa682-76ae-4979-a40c-c1eae1121f3c.jpg?1591226372", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/e/febfa682-76ae-4979-a40c-c1eae1121f3c.jpg?1591226372"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Kindred Denial", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/f/4fbdeac6-f61b-4669-934c-9216d669500f.jpg?1645417342", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/f/4fbdeac6-f61b-4669-934c-9216d669500f.jpg?1645417342"}, "reprint": false, "digital": true, "set_type": "alchemy"}, {"name": "Lapse of Certainty", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/c/ec609036-dfbf-47de-9a3a-762aea4196d4.jpg?1562804498", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/c/ec609036-dfbf-47de-9a3a-762aea4196d4.jpg?1562804498"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Laquatus's Disdain", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/2/e2ea5448-2d72-42eb-814c-197153d8e06a.jpg?1562632366", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/2/e2ea5448-2d72-42eb-814c-197153d8e06a.jpg?1562632366"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Last Word", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/3/139d2ece-f656-4cac-8d77-b0f083f76c70.jpg?1562635496", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/3/139d2ece-f656-4cac-8d77-b0f083f76c70.jpg?1562635496"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Lay Bare", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/4/0454c2a8-b17d-4cdf-8562-9a28bc6cf0be.jpg?1562700738", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/4/0454c2a8-b17d-4cdf-8562-9a28bc6cf0be.jpg?1562700738"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Liquify", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/2/12fadf25-0995-440d-a3e6-7964ed86cff6.jpg?1562628664", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/2/12fadf25-0995-440d-a3e6-7964ed86cff6.jpg?1562628664"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Lofty Denial", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/4/64832674-beb1-446e-b2f7-8a5e271139a5.jpg?1616182218", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/4/64832674-beb1-446e-b2f7-8a5e271139a5.jpg?1616182218"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Logic Knot", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/2/624feb0e-f683-4eb6-a63b-7872d0e28f1f.jpg?1619394325", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/2/624feb0e-f683-4eb6-a63b-7872d0e28f1f.jpg?1619394325"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Logic Knot", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/e/4e946be1-4ed6-4e2c-9782-3f630f8a8e1f.jpg?1562910897", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/e/4e946be1-4ed6-4e2c-9782-3f630f8a8e1f.jpg?1562910897"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Lookout's Dispersal", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/5/f5751a3c-7695-4c47-9cbd-92fd5b1b7ec9.jpg?1562566719", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/5/f5751a3c-7695-4c47-9cbd-92fd5b1b7ec9.jpg?1562566719"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Lose Focus", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/8/985bdb0c-ce6c-4506-8163-76f3b2fdf5fb.jpg?1626094565", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/8/985bdb0c-ce6c-4506-8163-76f3b2fdf5fb.jpg?1626094565"}, "reprint": false, "digital": false, "set_type": "draft_innovation"}, {"name": "Lost in the Mist", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/e/1e5fc39d-590a-436b-ab90-a1741d2ae3da.jpg?1562827161", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/e/1e5fc39d-590a-436b-ab90-a1741d2ae3da.jpg?1562827161"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Mages' Contest", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/5/c516861c-68d9-4d02-a343-689dba0526c6.jpg?1562934507", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/5/c516861c-68d9-4d02-a343-689dba0526c6.jpg?1562934507"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Make Disappear", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/f/3f2d6a21-ea77-484b-9e3a-1bd49806f907.jpg?1649471769", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/f/3f2d6a21-ea77-484b-9e3a-1bd49806f907.jpg?1649471769"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Mana Drain", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/1/416d2d51-8f29-4e95-b037-e8c32b081e6c.jpg?1562848002", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/1/416d2d51-8f29-4e95-b037-e8c32b081e6c.jpg?1562848002"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Mana Drain", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/c/cc9a04dc-afee-4194-80f5-fb1d9c906de7.jpg?1562936126", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/c/cc9a04dc-afee-4194-80f5-fb1d9c906de7.jpg?1562936126"}, "reprint": true, "digital": true, "set_type": "masters"}, {"name": "Mana Drain", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/6/e691adef-3027-4e6a-889f-9f4e2df36a7c.jpg?1562861377", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/6/e691adef-3027-4e6a-889f-9f4e2df36a7c.jpg?1562861377"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Mana Drain", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/5/456a2f03-8304-4512-804c-76653e30f436.jpg?1655827521", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/5/456a2f03-8304-4512-804c-76653e30f436.jpg?1655827521"}, "reprint": true, "frame_effects": ["inverted"], "digital": false, "set_type": "masters"}, {"name": "Mana Leak", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/7/a7c7757d-8036-4b33-a1cb-07795d392588.jpg?1562470857", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/7/a7c7757d-8036-4b33-a1cb-07795d392588.jpg?1562470857"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Mana Leak", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/b/abcaf16d-aa02-43e2-aa38-bb1835d47a05.jpg?1562597349", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/b/abcaf16d-aa02-43e2-aa38-bb1835d47a05.jpg?1562597349"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Mana Leak", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/e/dea41eb7-5828-4735-bca1-0dbb0fda04e3.jpg?1561758236", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/e/dea41eb7-5828-4735-bca1-0dbb0fda04e3.jpg?1561758236"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Mana Tithe", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/d/7d48d622-f397-4f31-b1a5-0c23f60aa71c.jpg?1562575298", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/d/7d48d622-f397-4f31-b1a5-0c23f60aa71c.jpg?1562575298"}, "reprint": false, "frame_effects": ["colorshifted"], "digital": false, "set_type": "expansion"}, {"name": "Mana Tithe", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/7/e7f32354-893d-4f0b-b555-e0757fb5443b.jpg?1623592291", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/7/e7f32354-893d-4f0b-b555-e0757fb5443b.jpg?1623592291"}, "reprint": true, "digital": false, "set_type": "masterpiece"}, {"name": "Mana Tithe", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/5/652b0ce3-293d-4599-8a04-9df01b9bc678.jpg?1561757305", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/5/652b0ce3-293d-4599-8a04-9df01b9bc678.jpg?1561757305"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Memory Drain", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/a/aadc1809-d6bb-455c-b6ce-dd11521808b6.jpg?1581479403", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/a/aadc1809-d6bb-455c-b6ce-dd11521808b6.jpg?1581479403"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Memory Lapse", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/0/30202613-d05f-4f47-af97-d0b75ccac293.jpg?1634131658", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/0/30202613-d05f-4f47-af97-d0b75ccac293.jpg?1634131658"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Memory Lapse", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/d/2d85cc30-ccae-4af8-834a-f7870dace679.jpg?1562235009", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/d/2d85cc30-ccae-4af8-834a-f7870dace679.jpg?1562235009"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Memory Lapse", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/3/63453ed9-5cf1-4cad-b173-a067f22a4405.jpg?1562719747", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/3/63453ed9-5cf1-4cad-b173-a067f22a4405.jpg?1562719747"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Memory Lapse", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/d/3d2cc591-3a81-468a-91a4-3c3aac83a21a.jpg?1562587259", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/d/3d2cc591-3a81-468a-91a4-3c3aac83a21a.jpg?1562587259"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Memory Lapse", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/c/6c8b5df3-6153-470e-be9c-f38d3cf66081.jpg?1562587296", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/c/6c8b5df3-6153-470e-be9c-f38d3cf66081.jpg?1562587296"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Memory Lapse", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/8/98c1b465-b6d9-491b-bfc2-c034cc825d27.jpg?1623592117", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/8/98c1b465-b6d9-491b-bfc2-c034cc825d27.jpg?1623592117"}, "reprint": true, "digital": false, "set_type": "masterpiece"}, {"name": "Mental Misstep", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/1/61e9c6df-1c84-4eab-9076-a4feb6347c10.jpg?1566819829", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/1/61e9c6df-1c84-4eab-9076-a4feb6347c10.jpg?1566819829"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Metallic Rebuke", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/7/f712ac26-dca4-459b-84c1-010597007f60.jpg?1576381519", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/7/f712ac26-dca4-459b-84c1-010597007f60.jpg?1576381519"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Minamo's Meddling", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/0/502c4aca-98f8-4c7d-89fd-ee42c938fac7.jpg?1562876978", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/0/502c4aca-98f8-4c7d-89fd-ee42c938fac7.jpg?1562876978"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Mindbreak Trap", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/f/4f51140b-6254-431a-8810-94307bfdfbbe.jpg?1562612097", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/f/4f51140b-6254-431a-8810-94307bfdfbbe.jpg?1562612097"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Mindstatic", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/5/55d3fad5-a12a-4b41-9c7b-c1af5e0b5ca8.jpg?1562910742", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/5/55d3fad5-a12a-4b41-9c7b-c1af5e0b5ca8.jpg?1562910742"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Mindswipe", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/5/557e8303-a021-4257-b41a-7d25f04618c8.jpg?1562786781", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/5/557e8303-a021-4257-b41a-7d25f04618c8.jpg?1562786781"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Miscalculation", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/b/4b4956a2-9a39-4152-9c98-70e4b2acfa26.jpg?1562862809", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/b/4b4956a2-9a39-4152-9c98-70e4b2acfa26.jpg?1562862809"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Miscast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/3/033afbd5-9937-4957-98ba-48e469a490bb.jpg?1594735579", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/3/033afbd5-9937-4957-98ba-48e469a490bb.jpg?1594735579"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Molten Influence", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/c/4c2b326b-d177-4a03-a0a3-fe2c2d4af272.jpg?1562908953", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/c/4c2b326b-d177-4a03-a0a3-fe2c2d4af272.jpg?1562908953"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Muddle the Mixture", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/c/4cc785b0-0a77-4b02-b0b4-2bda2fc621cc.jpg?1598914378", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/c/4cc785b0-0a77-4b02-b0b4-2bda2fc621cc.jpg?1598914378"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Mystical Dispute", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/b/fbe04cb8-a8b9-4241-baae-b398a2509a3a.jpg?1572489956", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/b/fbe04cb8-a8b9-4241-baae-b398a2509a3a.jpg?1572489956"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Mystic Confluence", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/1/81bbffc2-6f58-4baa-8f95-168eab106b15.jpg?1562706477", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/1/81bbffc2-6f58-4baa-8f95-168eab106b15.jpg?1562706477"}, "reprint": false, "digital": false, "set_type": "commander"}, {"name": "Mystic Denial", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/2/1296ddc4-300d-44f6-95d8-1b392613d379.jpg?1562255840", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/2/1296ddc4-300d-44f6-95d8-1b392613d379.jpg?1562255840"}, "reprint": true, "digital": false, "set_type": "starter"}, {"name": "Mystic Denial", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/0/30bb424f-f3d6-4616-a368-df12af3ad024.jpg?1562906405", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/0/30bb424f-f3d6-4616-a368-df12af3ad024.jpg?1562906405"}, "reprint": true, "digital": false, "set_type": "starter"}, {"name": "Mystic Denial", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/2/52d60f29-6da0-4ce6-9c92-96f313007271.jpg?1562446637", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/2/52d60f29-6da0-4ce6-9c92-96f313007271.jpg?1562446637"}, "reprint": false, "digital": false, "set_type": "starter"}, {"name": "Mystic Genesis", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/e/ae1dd1ac-1a1e-485d-a11f-d1323a69f95e.jpg?1561841867", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/e/ae1dd1ac-1a1e-485d-a11f-d1323a69f95e.jpg?1561841867"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Narset's Reversal", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/3/63754036-d51e-47bb-925b-564d9dc922ff.jpg?1557576279", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/3/63754036-d51e-47bb-925b-564d9dc922ff.jpg?1557576279"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Negate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/9/e92c7477-d453-4fa4-acf4-3835ab9eb55a.jpg?1604194548", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/9/e92c7477-d453-4fa4-acf4-3835ab9eb55a.jpg?1604194548"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Negate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/1/31534f45-43e6-4103-bf58-ad8fa688e4b0.jpg?1555039942", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/1/31534f45-43e6-4103-bf58-ad8fa688e4b0.jpg?1555039942"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Negate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/b/cb142515-0856-441d-84d4-9c9d450a86e9.jpg?1576381530", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/b/cb142515-0856-441d-84d4-9c9d450a86e9.jpg?1576381530"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Negate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/2/026c499d-3d5b-4f65-a824-f78f146b82ef.jpg?1562895467", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/2/026c499d-3d5b-4f65-a824-f78f146b82ef.jpg?1562895467"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Negate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/0/60380ed0-fed1-4d68-9763-56a9ff8ac5e6.jpg?1562787156", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/0/60380ed0-fed1-4d68-9763-56a9ff8ac5e6.jpg?1562787156"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Negate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/a/5a501252-e722-4ebf-bcf7-f53a42745fa7.jpg?1562878670", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/a/5a501252-e722-4ebf-bcf7-f53a42745fa7.jpg?1562878670"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Negate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/b/5bfe3a17-3349-4fcc-a9b5-418faa55cc43.jpg?1623592516", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/b/5bfe3a17-3349-4fcc-a9b5-418faa55cc43.jpg?1623592516"}, "reprint": true, "digital": false, "set_type": "masterpiece"}, {"name": "Negate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/8/9850fbe9-68d2-4952-b48d-4737cef34f4a.jpg?1561757632", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/8/9850fbe9-68d2-4952-b48d-4737cef34f4a.jpg?1561757632"}, "reprint": true, "digital": false, "set_type": "spellbook"}, {"name": "Negate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/2/226e5187-d285-4547-869d-761fdbee6f1b.jpg?1561756781", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/2/226e5187-d285-4547-869d-761fdbee6f1b.jpg?1561756781"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Neutralize", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/4/0430da3c-9460-4b62-ae28-2e7e6f4d06a4.jpg?1591226400", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/4/0430da3c-9460-4b62-ae28-2e7e6f4d06a4.jpg?1591226400"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Neutralizing Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/5/e549a8fc-6001-43db-88b1-ce8ed42a3443.jpg?1562830917", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/5/e549a8fc-6001-43db-88b1-ce8ed42a3443.jpg?1562830917"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Nix", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/d/3dab4f64-2a91-409a-b83b-45b22afd22ff.jpg?1562907421", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/d/3dab4f64-2a91-409a-b83b-45b22afd22ff.jpg?1562907421"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "No Escape", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/c/bc9888a1-6f35-4802-b8fb-902017736d4a.jpg?1557576285", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/c/bc9888a1-6f35-4802-b8fb-902017736d4a.jpg?1557576285"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Not of This World", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/6/569e2c39-7a49-4a3b-afe5-1862a7da8026.jpg?1562704022", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/6/569e2c39-7a49-4a3b-afe5-1862a7da8026.jpg?1562704022"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Nullify", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/9/a940d859-3fb1-4946-8277-b7c503605b1e.jpg?1593091715", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/9/a940d859-3fb1-4946-8277-b7c503605b1e.jpg?1593091715"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Obscura Charm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/9/9961562d-cad9-40e5-afae-3ebce77a2260.jpg?1648583418", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/9/9961562d-cad9-40e5-afae-3ebce77a2260.jpg?1648583418"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Obscura Charm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/a/4a02b758-65b6-4c25-83b9-de63a1a92b51.jpg?1648583494", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/a/4a02b758-65b6-4c25-83b9-de63a1a92b51.jpg?1648583494"}, "reprint": false, "frame_effects": ["showcase"], "digital": false, "set_type": "expansion"}, {"name": "Offering to Asha", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/6/260fe443-ca03-42b1-bcee-86e5173c1aaf.jpg?1562640177", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/6/260fe443-ca03-42b1-bcee-86e5173c1aaf.jpg?1562640177"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Ojutai's Command", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/7/c7a7f500-594d-4c7b-80e8-54ae1ada2444.jpg?1562792959", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/7/c7a7f500-594d-4c7b-80e8-54ae1ada2444.jpg?1562792959"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Ojutai's Command", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/3/939778a2-a10d-4dd4-8f78-0c366b76bf81.jpg?1562876267", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/3/939778a2-a10d-4dd4-8f78-0c366b76bf81.jpg?1562876267"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Oppressive Will", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/b/abcb5e75-c7a1-41de-a952-05aefb115270.jpg?1562495576", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/b/abcb5e75-c7a1-41de-a952-05aefb115270.jpg?1562495576"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Out of Bounds", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/b/8b457672-902b-42c0-9d53-a3c21be2f500.jpg?1562923137", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/b/8b457672-902b-42c0-9d53-a3c21be2f500.jpg?1562923137"}, "reprint": false, "digital": false, "set_type": "draft_innovation"}, {"name": "Outwit", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/2/429f7cf0-579a-4003-b5cf-4baf5d420796.jpg?1592708662", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/2/429f7cf0-579a-4003-b5cf-4baf5d420796.jpg?1592708662"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Override", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/5/35964fa6-800d-41d6-9f82-fb9c87deee56.jpg?1562140248", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/5/35964fa6-800d-41d6-9f82-fb9c87deee56.jpg?1562140248"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Overrule", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/2/22b83a31-f974-4a49-b9ee-92f7767f11e0.jpg?1593273676", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/2/22b83a31-f974-4a49-b9ee-92f7767f11e0.jpg?1593273676"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Overwhelming Denial", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/3/33ff1000-1a4e-43f6-aa02-1dbe9fac6901.jpg?1562905471", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/3/33ff1000-1a4e-43f6-aa02-1dbe9fac6901.jpg?1562905471"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Overwhelming Intellect", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/b/cbeea686-7efc-48f5-b90b-bf1befc76a30.jpg?1562496066", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/b/cbeea686-7efc-48f5-b90b-bf1befc76a30.jpg?1562496066"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Pact of Negation", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/c/cca467a2-a2b3-4bdf-9d60-62979f675347.jpg?1562936138", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/c/cca467a2-a2b3-4bdf-9d60-62979f675347.jpg?1562936138"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Pact of Negation", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/a/3ab90299-547a-4538-a31c-f55afab10c50.jpg?1562906886", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/a/3ab90299-547a-4538-a31c-f55afab10c50.jpg?1562906886"}, "reprint": true, "digital": false, "set_type": "masterpiece"}, {"name": "Perplex", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/d/0db57459-29f0-4ef6-b256-56955036c0ef.jpg?1598917204", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/d/0db57459-29f0-4ef6-b256-56955036c0ef.jpg?1598917204"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Plasm Capture", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/f/0ffe8485-d5fb-47cc-af53-6e0fd062b7a2.jpg?1562898119", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/f/0ffe8485-d5fb-47cc-af53-6e0fd062b7a2.jpg?1562898119"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Power Sink", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/6/662cf693-18c4-4169-bcce-09862778f60c.jpg?1562916378", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/6/662cf693-18c4-4169-bcce-09862778f60c.jpg?1562916378"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Power Sink", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/b/abc58c34-c3de-47f8-a42f-3a974dcb9c47.jpg?1562055922", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/b/abc58c34-c3de-47f8-a42f-3a974dcb9c47.jpg?1562055922"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Power Sink", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/9/49717583-e0bb-47d6-92d0-8959af13391f.jpg?1562718814", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/9/49717583-e0bb-47d6-92d0-8959af13391f.jpg?1562718814"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Power Sink", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/5/85cbec45-81b4-40cc-b356-d6713a6a9b2b.jpg?1562919825", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/5/85cbec45-81b4-40cc-b356-d6713a6a9b2b.jpg?1562919825"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Power Sink", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/b/1b342dd3-09b9-4108-bf12-a65d4cef4eb9.jpg?1559591331", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/b/1b342dd3-09b9-4108-bf12-a65d4cef4eb9.jpg?1559591331"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Preemptive Strike", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/2/c2314bf1-b22d-48c2-860f-e1081f56296b.jpg?1562257530", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/2/c2314bf1-b22d-48c2-860f-e1081f56296b.jpg?1562257530"}, "reprint": false, "digital": false, "set_type": "starter"}, {"name": "Prohibit", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/d/0daa5458-2a97-40d0-b18d-2381a7a68ee1.jpg?1562897807", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/d/0daa5458-2a97-40d0-b18d-2381a7a68ee1.jpg?1562897807"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Psychic Barrier", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/c/1cba7d67-5c6c-4738-8907-7cce503e3180.jpg?1562875859", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/c/1cba7d67-5c6c-4738-8907-7cce503e3180.jpg?1562875859"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Psychic Rebuttal", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/7/67a105f8-0c01-4c09-a3bf-8c912b6dc741.jpg?1562023585", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/7/67a105f8-0c01-4c09-a3bf-8c912b6dc741.jpg?1562023585"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Psychic Strike", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/d/0d87927c-80a6-4146-92a5-58c510ce7958.jpg?1561815780", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/d/0d87927c-80a6-4146-92a5-58c510ce7958.jpg?1561815780"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Psychic Trance", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/5/d5e55695-16cc-4373-8078-959f1ded4c6d.jpg?1562945989", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/5/d5e55695-16cc-4373-8078-959f1ded4c6d.jpg?1562945989"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Punish Ignorance", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/b/9bc37d01-ffe5-4dfe-b59e-204df82d1d36.jpg?1562707043", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/b/9bc37d01-ffe5-4dfe-b59e-204df82d1d36.jpg?1562707043"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Put Away", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/1/c17dff9e-23f7-4b12-95e7-aa1c00ab3d18.jpg?1562835533", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/1/c17dff9e-23f7-4b12-95e7-aa1c00ab3d18.jpg?1562835533"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Pyroblast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/0/b029eb9a-dd7a-40c2-96c4-0063d9cc002c.jpg?1580014621", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/0/b029eb9a-dd7a-40c2-96c4-0063d9cc002c.jpg?1580014621"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Pyroblast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/3/c342cac5-08ae-4428-9c2c-f6c5904e54d2.jpg?1562931528", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/3/c342cac5-08ae-4428-9c2c-f6c5904e54d2.jpg?1562931528"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Pyroblast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/3/93c460dc-cef2-4345-b9b8-a774307ba2d6.jpg?1593559584", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/3/93c460dc-cef2-4345-b9b8-a774307ba2d6.jpg?1593559584"}, "reprint": true, "digital": false, "set_type": "spellbook"}, {"name": "Pyroblast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/3/33afbf78-7a50-48e0-bec8-656f571759e2.jpg?1562543945", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/3/33afbf78-7a50-48e0-bec8-656f571759e2.jpg?1562543945"}, "reprint": true, "digital": true, "set_type": "promo"}, {"name": "Quandrix Command", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/2/021b62d8-d160-47f5-bc51-0474f160d13f.jpg?1624739521", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/2/021b62d8-d160-47f5-bc51-0474f160d13f.jpg?1624739521"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Quash", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/8/48ca8c31-a9ea-4388-b257-951c1c68b86d.jpg?1562876834", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/8/48ca8c31-a9ea-4388-b257-951c1c68b86d.jpg?1562876834"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Quash", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/2/62019ac4-a5a1-4a8c-bfb4-96e818949bbe.jpg?1562444219", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/2/62019ac4-a5a1-4a8c-bfb4-96e818949bbe.jpg?1562444219"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Quench", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/e/ee0ba01b-de96-4f8f-9405-ff3ad288afac.jpg?1589832153", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/e/ee0ba01b-de96-4f8f-9405-ff3ad288afac.jpg?1589832153"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Rakshasa's Disdain", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/d/bd9e8a25-2e71-431b-897f-8b62520a3ce9.jpg?1562829343", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/d/bd9e8a25-2e71-431b-897f-8b62520a3ce9.jpg?1562829343"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Rebuff the Wicked", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/a/fa47fcce-d4c4-40a2-8853-6d7569d50926.jpg?1562586538", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/a/fa47fcce-d4c4-40a2-8853-6d7569d50926.jpg?1562586538"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Red Elemental Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/0/70a45e9b-699e-425a-9f3d-267274830d3e.jpg?1562436618", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/0/70a45e9b-699e-425a-9f3d-267274830d3e.jpg?1562436618"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Red Elemental Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/7/776ad9be-3309-4f1d-9f27-6219d9477662.jpg?1559591383", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/7/776ad9be-3309-4f1d-9f27-6219d9477662.jpg?1559591383"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Red Elemental Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/c/6cdd2a7c-001d-4891-8513-4b6d96968b35.jpg?1562545467", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/c/6cdd2a7c-001d-4891-8513-4b6d96968b35.jpg?1562545467"}, "reprint": true, "digital": true, "set_type": "promo"}, {"name": "Reinterpret", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/6/765e64ae-699c-46bd-a8cc-c8c1075d644f.jpg?1625192562", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/6/765e64ae-699c-46bd-a8cc-c8c1075d644f.jpg?1625192562"}, "reprint": false, "digital": false, "set_type": "commander"}, {"name": "Reject", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/7/d77f0731-fb40-4dc2-8530-afcb5ce1f27f.jpg?1624661968", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/7/d77f0731-fb40-4dc2-8530-afcb5ce1f27f.jpg?1624661968"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Remand", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/0/0027e5ca-8046-40a0-bd73-79be55f28bff.jpg?1592754515", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/0/0027e5ca-8046-40a0-bd73-79be55f28bff.jpg?1592754515"}, "reprint": true, "digital": false, "set_type": "duel_deck"}, {"name": "Remand", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/8/581f3780-c480-48c6-b15c-1618f2feccb9.jpg?1598914434", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/8/581f3780-c480-48c6-b15c-1618f2feccb9.jpg?1598914434"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Remand", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/4/d41e8cc0-4e05-412b-8ea3-d5b5c45da601.jpg?1562164467", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/4/d41e8cc0-4e05-412b-8ea3-d5b5c45da601.jpg?1562164467"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Remove Soul", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/2/f25f4f0e-bbf4-46b1-97fd-e796ff9e138f.jpg?1562251278", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/2/f25f4f0e-bbf4-46b1-97fd-e796ff9e138f.jpg?1562251278"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Remove Soul", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/d/fd6bbb81-b830-4b22-be9a-852d9edbda21.jpg?1562595434", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/d/fd6bbb81-b830-4b22-be9a-852d9edbda21.jpg?1562595434"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Remove Soul", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/3/63de147c-2e62-41b9-8ada-93406387f08b.jpg?1562859196", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/3/63de147c-2e62-41b9-8ada-93406387f08b.jpg?1562859196"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Remove Soul", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/7/675440ff-9701-4310-a4ad-8502b9cb73ae.jpg?1561757323", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/7/675440ff-9701-4310-a4ad-8502b9cb73ae.jpg?1561757323"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Render Silent", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/3/e3f3d6e4-0abe-4042-a7f6-0395683e8582.jpg?1562937631", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/3/e3f3d6e4-0abe-4042-a7f6-0395683e8582.jpg?1562937631"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Render Silent", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/5/4514a13f-5eee-49a8-876c-6b4befff4592.jpg?1561757030", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/5/4514a13f-5eee-49a8-876c-6b4befff4592.jpg?1561757030"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Repel Intruders", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/8/38e64b09-1a58-4669-b7f2-baa3ccc85f2d.jpg?1568911006", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/8/38e64b09-1a58-4669-b7f2-baa3ccc85f2d.jpg?1568911006"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Rethink", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/1/915ae03f-22f3-4ecc-a875-5226d8dec384.jpg?1562921984", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/1/915ae03f-22f3-4ecc-a875-5226d8dec384.jpg?1562921984"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Revolutionary Rebuff", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/e/6ea63dad-6afe-464e-ab19-fabd9709c6f9.jpg?1576381387", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/e/6ea63dad-6afe-464e-ab19-fabd9709c6f9.jpg?1576381387"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Rewind", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/e/9e51c4fb-fb29-4b1c-b78e-1fadf94fc9a5.jpg?1562928379", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/e/9e51c4fb-fb29-4b1c-b78e-1fadf94fc9a5.jpg?1562928379"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Rites of Refusal", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/a/fa88f595-1b6f-4af0-bc50-bd07c8be431f.jpg?1562942139", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/a/fa88f595-1b6f-4af0-bc50-bd07c8be431f.jpg?1562942139"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Runeboggle", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/7/37b2fb23-f8b5-4f83-9b29-b18507acaa1a.jpg?1593272065", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/7/37b2fb23-f8b5-4f83-9b29-b18507acaa1a.jpg?1593272065"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Rune Snag", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/5/45b6cadf-1974-47c8-98d8-ba413486c3b5.jpg?1593275010", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/5/45b6cadf-1974-47c8-98d8-ba413486c3b5.jpg?1593275010"}, "reprint": false, "digital": false, "set_type": "expansion"}]}
\ No newline at end of file
diff --git a/web/public/mtg/jsons/counterspell3.json b/web/public/mtg/jsons/counterspell3.json
new file mode 100644
index 00000000..97aeadc5
--- /dev/null
+++ b/web/public/mtg/jsons/counterspell3.json
@@ -0,0 +1 @@
+{"has_more": false, "data": [{"name": "Rust", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/d/ad4974c8-34c5-4290-b325-7586a67f6d56.jpg?1592364545", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/d/ad4974c8-34c5-4290-b325-7586a67f6d56.jpg?1592364545"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Sage's Dousing", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/5/75ccd5f6-b363-433f-9e98-f65e10b10bc9.jpg?1562879335", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/5/75ccd5f6-b363-433f-9e98-f65e10b10bc9.jpg?1562879335"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Saw It Coming", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/7/877a1bb9-5eae-453a-bec0-a9de20ea6815.jpg?1631047574", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/7/877a1bb9-5eae-453a-bec0-a9de20ea6815.jpg?1631047574"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Scatter Arc", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/2/32ed969f-2c8e-4421-9448-dc5a2afdc81d.jpg?1561821983", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/2/32ed969f-2c8e-4421-9448-dc5a2afdc81d.jpg?1561821983"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Scattering Stroke", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/5/c536c1ce-a012-4d77-ab29-8574be164731.jpg?1562367009", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/5/c536c1ce-a012-4d77-ab29-8574be164731.jpg?1562367009"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Scatter to the Winds", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/7/d73ad49f-fe15-4fe5-9731-fd71d31c1e7f.jpg?1562946348", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/7/d73ad49f-fe15-4fe5-9731-fd71d31c1e7f.jpg?1562946348"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Scent of Brine", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/1/d117bf8d-23ec-4f9d-99d0-3a990c5f7075.jpg?1562445215", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/1/d117bf8d-23ec-4f9d-99d0-3a990c5f7075.jpg?1562445215"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Second Guess", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/d/0d22d093-8e89-4d54-ac04-14c8759de3ea.jpg?1592708686", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/d/0d22d093-8e89-4d54-ac04-14c8759de3ea.jpg?1592708686"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Silumgar's Command", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/a/ba26dbbc-d4a2-44a1-8e6b-affe61f43a34.jpg?1562792137", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/a/ba26dbbc-d4a2-44a1-8e6b-affe61f43a34.jpg?1562792137"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Silumgar's Scorn", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/7/077bee72-62f6-4d90-8557-ff9cac42ec9a.jpg?1562782102", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/7/077bee72-62f6-4d90-8557-ff9cac42ec9a.jpg?1562782102"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Sinister Sabotage", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/c/6cbef36d-7170-424f-8fb1-8e7e112b7f0b.jpg?1572892841", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/c/6cbef36d-7170-424f-8fb1-8e7e112b7f0b.jpg?1572892841"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Soul Manipulation", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/c/bcd3cb05-c6f9-435a-a0e7-1f85da4a36eb.jpg?1562643969", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/c/bcd3cb05-c6f9-435a-a0e7-1f85da4a36eb.jpg?1562643969"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Spell Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/2/42d7af6a-bfd1-4e89-965a-68336507a9ee.jpg?1562828463", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/2/42d7af6a-bfd1-4e89-965a-68336507a9ee.jpg?1562828463"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Spell Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/f/5fe58a24-f6a6-4858-82a5-0ca1d524efe1.jpg?1562054243", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/f/5fe58a24-f6a6-4858-82a5-0ca1d524efe1.jpg?1562054243"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Spell Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/0/70e4584f-6e44-4ff8-8313-c8791e0156af.jpg?1562591827", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/0/70e4584f-6e44-4ff8-8313-c8791e0156af.jpg?1562591827"}, "reprint": true, "digital": false, "set_type": "core"}, {"name": "Spell Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/4/845734da-ab03-4dbc-bb5f-96481d3b8e88.jpg?1559591342", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/4/845734da-ab03-4dbc-bb5f-96481d3b8e88.jpg?1559591342"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Spell Burst", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/1/8169929c-641f-41c8-a48e-1a7d0c57726b.jpg?1619394723", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/1/8169929c-641f-41c8-a48e-1a7d0c57726b.jpg?1619394723"}, "reprint": true, "digital": false, "set_type": "masters"}, {"name": "Spell Burst", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/9/f95c8015-fd7d-4329-ab23-aec37a824083.jpg?1562947751", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/9/f95c8015-fd7d-4329-ab23-aec37a824083.jpg?1562947751"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Spell Contortion", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/b/4b748d8b-898f-4b55-bc33-f5bbbc823c45.jpg?1562286779", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/b/4b748d8b-898f-4b55-bc33-f5bbbc823c45.jpg?1562286779"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Spell Counter", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/3/e3d323f0-334f-49d1-b338-24c4b854a112.jpg?1562489832", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/3/e3d323f0-334f-49d1-b338-24c4b854a112.jpg?1562489832"}, "reprint": false, "digital": false, "set_type": "funny"}, {"name": "Spell Crumple", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/2/2247df4a-c5d8-4b34-b3a6-3c958eb65f94.jpg?1592713127", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/2/2247df4a-c5d8-4b34-b3a6-3c958eb65f94.jpg?1592713127"}, "reprint": false, "digital": false, "set_type": "commander"}, {"name": "Spelljack", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/e/3eda8c7b-ce35-482a-bece-52a30cc78a9a.jpg?1562629500", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/e/3eda8c7b-ce35-482a-bece-52a30cc78a9a.jpg?1562629500"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Spell Pierce", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/e/beb42273-935b-4bda-849e-c163606cf89e.jpg?1654566963", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/e/beb42273-935b-4bda-849e-c163606cf89e.jpg?1654566963"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Spell Pierce", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/b/6bf4dfc0-c58b-4535-b660-54ceaa6e0217.jpg?1562557054", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/b/6bf4dfc0-c58b-4535-b660-54ceaa6e0217.jpg?1562557054"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Spell Pierce", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/b/cb3d3901-e4a6-45ab-a7b5-c65d91e1875e.jpg?1562616640", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/b/cb3d3901-e4a6-45ab-a7b5-c65d91e1875e.jpg?1562616640"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Spell Pierce", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/3/d3c8f1c8-2b57-41a3-abeb-77ac7de62fa1.jpg?1656006437", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/3/d3c8f1c8-2b57-41a3-abeb-77ac7de62fa1.jpg?1656006437"}, "reprint": true, "frame_effects": ["inverted"], "digital": false, "set_type": "masters"}, {"name": "Spell Pierce", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/4/a4f8b11a-6b21-4532-96c9-bdb2cad603e8.jpg?1599332212", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/4/a4f8b11a-6b21-4532-96c9-bdb2cad603e8.jpg?1599332212"}, "reprint": true, "digital": false, "set_type": "box"}, {"name": "Spell Pierce", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/e/eef1f68a-b27c-4e81-9a3c-dccb86771bec.jpg?1562942998", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/e/eef1f68a-b27c-4e81-9a3c-dccb86771bec.jpg?1562942998"}, "reprint": true, "digital": false, "set_type": "masterpiece"}, {"name": "Spell Rupture", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/2/7267fcec-0879-4743-a45f-35057ccb2596.jpg?1561831328", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/2/7267fcec-0879-4743-a45f-35057ccb2596.jpg?1561831328"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Spellshift", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/5/f5c897a6-5835-42ac-8cc7-e8d9fc1e7c77.jpg?1562586074", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/5/f5c897a6-5835-42ac-8cc7-e8d9fc1e7c77.jpg?1562586074"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Spell Shrivel", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/f/efa110cb-f091-48f0-bc62-80f5f18568e8.jpg?1562951938", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/f/efa110cb-f091-48f0-bc62-80f5f18568e8.jpg?1562951938"}, "reprint": false, "frame_effects": ["devoid"], "digital": false, "set_type": "expansion"}, {"name": "Spell Snare", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/5/35554fdf-c70a-4baa-a35a-414caa9978be.jpg?1593272766", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/5/35554fdf-c70a-4baa-a35a-414caa9978be.jpg?1593272766"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Spell Snip", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/6/d6870203-ece9-4fe0-912b-2dcf685f3eb0.jpg?1562709543", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/6/d6870203-ece9-4fe0-912b-2dcf685f3eb0.jpg?1562709543"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Spell Snuff", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/f/efadce19-07f4-47af-abc0-a436bafcdd65.jpg?1562201508", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/f/efadce19-07f4-47af-abc0-a436bafcdd65.jpg?1562201508"}, "reprint": false, "digital": false, "set_type": "draft_innovation"}, {"name": "Spell Suck", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/6/f631bd92-2046-468d-8b10-d583a318ed24.jpg?1562946926", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/6/f631bd92-2046-468d-8b10-d583a318ed24.jpg?1562946926"}, "reprint": false, "digital": false, "set_type": "funny"}, {"name": "Spell Swindle", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/e/6e619ada-e9ce-4758-afd8-8def853877eb.jpg?1562557238", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/e/6e619ada-e9ce-4758-afd8-8def853877eb.jpg?1562557238"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Spell Syphon", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/8/b883113c-e52b-4633-b4a4-016093327b6a.jpg?1562835117", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/8/b883113c-e52b-4633-b4a4-016093327b6a.jpg?1562835117"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Split Decision", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/3/83ed7ebe-48be-4e6e-a293-b81484f85142.jpg?1562865914", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/3/83ed7ebe-48be-4e6e-a293-b81484f85142.jpg?1562865914"}, "reprint": false, "digital": false, "set_type": "draft_innovation"}, {"name": "Squelch", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/9/29421dd2-70a7-4623-afe0-ca4cb415ec87.jpg?1562758853", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/9/29421dd2-70a7-4623-afe0-ca4cb415ec87.jpg?1562758853"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Statute of Denial", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/f/af13770d-dddb-4b78-9cd3-4a0dc50472f4.jpg?1562792750", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/f/af13770d-dddb-4b78-9cd3-4a0dc50472f4.jpg?1562792750"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Steel Sabotage", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/b/bb40de7c-1905-4615-844b-4abc231fb01e.jpg?1562614249", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/b/bb40de7c-1905-4615-844b-4abc231fb01e.jpg?1562614249"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Stifle", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/d/2d7643c0-b2db-478f-944e-b27b77bad3eb.jpg?1562527068", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/d/2d7643c0-b2db-478f-944e-b27b77bad3eb.jpg?1562527068"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Stifle", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/a/ea24228f-da16-46eb-9dcf-a377286b6168.jpg?1562942013", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/a/ea24228f-da16-46eb-9dcf-a377286b6168.jpg?1562942013"}, "reprint": true, "digital": false, "set_type": "masterpiece"}, {"name": "Stifle", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/6/c6228e16-72d4-4771-9e3f-a83ec856d315.jpg?1562636845", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/6/c6228e16-72d4-4771-9e3f-a83ec856d315.jpg?1562636845"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Stoic Rebuttal", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/2/f2805239-f30a-4eca-a10b-41673daaa287.jpg?1562825062", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/2/f2805239-f30a-4eca-a10b-41673daaa287.jpg?1562825062"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Stubborn Denial", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/f/6f8626c4-306f-4e9d-8840-2bb73fe87e87.jpg?1562788344", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/f/6f8626c4-306f-4e9d-8840-2bb73fe87e87.jpg?1562788344"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Stymied Hopes", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/7/5702b757-5be5-4a48-bc73-a87ec4f3193b.jpg?1562818334", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/7/5702b757-5be5-4a48-bc73-a87ec4f3193b.jpg?1562818334"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Sublime Epiphany", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/d/ad1bcb44-a562-4f66-b862-6d0ef3546ab4.jpg?1594735795", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/d/ad1bcb44-a562-4f66-b862-6d0ef3546ab4.jpg?1594735795"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Suffocating Blast", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/2/c2a70297-2a7b-4a0c-ace5-cd61bfe6dafd.jpg?1562940975", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/2/c2a70297-2a7b-4a0c-ace5-cd61bfe6dafd.jpg?1562940975"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Summary Dismissal", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/b/0b75794d-3334-4b4d-9446-0a251dd3bd15.jpg?1576384222", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/b/0b75794d-3334-4b4d-9446-0a251dd3bd15.jpg?1576384222"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Summoner's Bane", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/d/ed82afba-df51-4bd9-853c-d3ef323095a6.jpg?1562618060", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/d/ed82afba-df51-4bd9-853c-d3ef323095a6.jpg?1562618060"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Supreme Will", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/6/b677e7cb-7b5d-4993-8f13-881493c498ce.jpg?1562811958", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/6/b677e7cb-7b5d-4993-8f13-881493c498ce.jpg?1562811958"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Swan Song", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/f/efd26041-059b-4a1e-9ce8-c3cfd69a3721.jpg?1562837218", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/f/efd26041-059b-4a1e-9ce8-c3cfd69a3721.jpg?1562837218"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Swan Song", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/0/40fc6412-df1c-4bfa-842b-8c3a6f14e19d.jpg?1599358784", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/0/40fc6412-df1c-4bfa-842b-8c3a6f14e19d.jpg?1599358784"}, "reprint": true, "digital": false, "set_type": "box"}, {"name": "Swift Silence", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/a/1/a1c5f733-e126-4c22-b528-18bdb90b509b.jpg?1593273784", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/a/1/a1c5f733-e126-4c22-b528-18bdb90b509b.jpg?1593273784"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Syncopate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/8/08375017-4432-4296-9799-966db145ed7c.jpg?1643588741", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/8/08375017-4432-4296-9799-966db145ed7c.jpg?1643588741"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Syncopate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/8/f81739a5-35a7-4812-a7af-e1951bf5579c.jpg?1617884773", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/8/f81739a5-35a7-4812-a7af-e1951bf5579c.jpg?1617884773"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Syncopate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/a/ba6f218f-83b0-4b68-a00f-0327cd79f32a.jpg?1562792232", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/a/ba6f218f-83b0-4b68-a00f-0327cd79f32a.jpg?1562792232"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Syncopate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/7/b7850794-4c85-4844-a461-650cd4eaec93.jpg?1562929140", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/7/b7850794-4c85-4844-a461-650cd4eaec93.jpg?1562929140"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Syphon Essence", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/3/435a2d31-ac2c-45aa-8369-6c2d6fbba4e4.jpg?1643588767", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/3/435a2d31-ac2c-45aa-8369-6c2d6fbba4e4.jpg?1643588767"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Tale's End", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/1/4/1421115b-9a98-4ab2-bcb2-7d8899ce12db.jpg?1592516519", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/1/4/1421115b-9a98-4ab2-bcb2-7d8899ce12db.jpg?1592516519"}, "reprint": false, "digital": false, "set_type": "core"}, {"name": "Teferi's Response", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/3/f3bb2df8-c559-4a34-83b0-d48fbc694cc8.jpg?1562944007", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/3/f3bb2df8-c559-4a34-83b0-d48fbc694cc8.jpg?1562944007"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Temur Charm", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/2/e2ee3e36-a849-42b0-b84b-027a08427c35.jpg?1562794960", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/2/e2ee3e36-a849-42b0-b84b-027a08427c35.jpg?1562794960"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Test of Talents", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/e/6e2b6236-b40c-430c-98b0-7940b942657a.jpg?1624590572", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/e/6e2b6236-b40c-430c-98b0-7940b942657a.jpg?1624590572"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Thassa's Intervention", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/c/2c1241d0-20d4-4eab-970d-74e476f023b4.jpg?1584279765", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/c/2c1241d0-20d4-4eab-970d-74e476f023b4.jpg?1584279765"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Thassa's Rebuff", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/1/816a6ff7-cede-4346-b3e6-aee33aefac3a.jpg?1593091807", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/1/816a6ff7-cede-4346-b3e6-aee33aefac3a.jpg?1593091807"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Thoughtbind", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/7/9/7919cf41-67bb-4dc4-90de-cf3fa2096c2e.jpg?1593860622", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/7/9/7919cf41-67bb-4dc4-90de-cf3fa2096c2e.jpg?1593860622"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Thought Collapse", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/4/948b569b-6341-418b-99b5-f79dfb3fe8dd.jpg?1584830401", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/4/948b569b-6341-418b-99b5-f79dfb3fe8dd.jpg?1584830401"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Thwart", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/1/c12a0717-e9ea-4be3-a29f-179671ed4489.jpg?1562383015", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/1/c12a0717-e9ea-4be3-a29f-179671ed4489.jpg?1562383015"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Tibalt's Trickery", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/d/dd921e27-3e08-438c-bec2-723226d35175.jpg?1652278784", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/d/dd921e27-3e08-438c-bec2-723226d35175.jpg?1652278784"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Time Stop", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/9/f968c5e9-12a8-4542-90b4-84e0238fa375.jpg?1562766084", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/9/f968c5e9-12a8-4542-90b4-84e0238fa375.jpg?1562766084"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Trap Essence", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/0/c063b2b8-5243-43a8-8cb0-927116003bda.jpg?1562701652", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/0/c063b2b8-5243-43a8-8cb0-927116003bda.jpg?1562701652"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Traumatic Visions", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/1/f1e8b03d-9265-4699-b626-5efa73292d43.jpg?1562804612", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/1/f1e8b03d-9265-4699-b626-5efa73292d43.jpg?1562804612"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Trickbind", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/2/f2e58ff2-dea3-42b3-8c22-3e6202a7d433.jpg?1562946300", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/2/f2e58ff2-dea3-42b3-8c22-3e6202a7d433.jpg?1562946300"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Turn Aside", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/b/3b7573c2-484c-4b4e-9c26-0f005bd1daee.jpg?1576384240", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/b/3b7573c2-484c-4b4e-9c26-0f005bd1daee.jpg?1576384240"}, "reprint": true, "digital": false, "set_type": "expansion"}, {"name": "Turn Aside", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/5/6/56226f57-6ff0-430e-aba6-6b3dd51f8d3c.jpg?1562817712", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/5/6/56226f57-6ff0-430e-aba6-6b3dd51f8d3c.jpg?1562817712"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Undermine", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/3/2334bc71-5f85-47ff-b393-601a1e746a4e.jpg?1562902053", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/3/2334bc71-5f85-47ff-b393-601a1e746a4e.jpg?1562902053"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Undersimplify", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/3/e/3eaebdc1-7a20-45db-9d45-0238fc917496.jpg?1656479084", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/3/e/3eaebdc1-7a20-45db-9d45-0238fc917496.jpg?1656479084"}, "reprint": false, "digital": true, "set_type": "alchemy"}, {"name": "Unified Will", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/c/6cb50db7-f1d4-4f9d-ac60-564398af79ea.jpg?1562704807", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/c/6cb50db7-f1d4-4f9d-ac60-564398af79ea.jpg?1562704807"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Unsubstantiate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/a/ba5dac3d-4b49-44c4-a7b2-0a99485252c9.jpg?1576384246", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/a/ba5dac3d-4b49-44c4-a7b2-0a99485252c9.jpg?1576384246"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Unsubstantiate", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/b/8b184d7e-46ae-450e-9228-eb605ac3ad41.jpg?1562924384", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/b/8b184d7e-46ae-450e-9228-eb605ac3ad41.jpg?1562924384"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Unwind", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/7/97da6607-9131-4f8b-8af3-63439a59b78b.jpg?1562739909", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/7/97da6607-9131-4f8b-8af3-63439a59b78b.jpg?1562739909"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Verdant Command", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/8/3/83031ea8-a6c9-4318-af16-bba701dd76bb.jpg?1626097990", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/8/3/83031ea8-a6c9-4318-af16-bba701dd76bb.jpg?1626097990"}, "reprint": false, "digital": false, "set_type": "draft_innovation"}, {"name": "Verdant Command", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/0/7/070a3f30-0839-4678-a37c-475ee189811e.jpg?1626101883", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/0/7/070a3f30-0839-4678-a37c-475ee189811e.jpg?1626101883"}, "reprint": false, "frame_effects": ["showcase"], "digital": false, "set_type": "draft_innovation"}, {"name": "Very Cryptic Command", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/8/d8e84dd2-01f9-4fad-8a24-cc86424d09a2.jpg?1562940811", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/8/d8e84dd2-01f9-4fad-8a24-cc86424d09a2.jpg?1562940811"}, "reprint": false, "digital": false, "set_type": "funny"}, {"name": "Vex", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/2/e28a9f15-5469-4dc2-8a73-646f854fec7e.jpg?1562640140", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/2/e28a9f15-5469-4dc2-8a73-646f854fec7e.jpg?1562640140"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Void Shatter", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/b/4bf13c5e-3968-48ad-ba08-99ba58873223.jpg?1562910363", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/b/4bf13c5e-3968-48ad-ba08-99ba58873223.jpg?1562910363"}, "reprint": false, "frame_effects": ["devoid"], "digital": false, "set_type": "expansion"}, {"name": "Voidslime", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/e/6/e640664f-5cc7-4970-b966-6e6e5ae09c5a.jpg?1640462194", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/e/6/e640664f-5cc7-4970-b966-6e6e5ae09c5a.jpg?1640462194"}, "reprint": true, "digital": false, "set_type": "box"}, {"name": "Voidslime", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/2/6/265c269e-1b5e-4e5f-873f-7733bd4142aa.jpg?1562384947", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/2/6/265c269e-1b5e-4e5f-873f-7733bd4142aa.jpg?1562384947"}, "reprint": true, "digital": false, "set_type": "promo"}, {"name": "Warping Wail", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/2/f2ef4db8-b51c-4f52-84f1-6fee31c4a14c.jpg?1562943843", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/2/f2ef4db8-b51c-4f52-84f1-6fee31c4a14c.jpg?1562943843"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Wash Away", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/4/3/43411ade-be80-4535-8baa-7055e78496df.jpg?1643588844", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/4/3/43411ade-be80-4535-8baa-7055e78496df.jpg?1643588844"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Whirlwind Denial", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/9/e/9e127856-bedd-40a9-9e8e-d1f9fbefe07d.jpg?1581479658", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/9/e/9e127856-bedd-40a9-9e8e-d1f9fbefe07d.jpg?1581479658"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Whirlwind Denial", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/f/7/f7a0c25a-8760-44ea-a418-fcd4a9761632.jpg?1623594049", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/f/7/f7a0c25a-8760-44ea-a418-fcd4a9761632.jpg?1623594049"}, "reprint": true, "digital": false, "set_type": "masterpiece"}, {"name": "Wild Ricochet", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/d/7/d76f09bc-b49a-4ad2-be2d-2a191d41b86d.jpg?1562370137", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/d/7/d76f09bc-b49a-4ad2-be2d-2a191d41b86d.jpg?1562370137"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Withering Boon", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/6/e/6e6499cb-6073-4c94-8c82-47f489094df5.jpg?1562719780", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/6/e/6e6499cb-6073-4c94-8c82-47f489094df5.jpg?1562719780"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "Wizard's Retort", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/b/a/bae30b7d-9306-46ef-adea-c4057f59c9c1.jpg?1562741944", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/b/a/bae30b7d-9306-46ef-adea-c4057f59c9c1.jpg?1562741944"}, "reprint": false, "digital": false, "set_type": "expansion"}, {"name": "You Find the Villains' Lair", "image_uris": {"normal": "https://c1.scryfall.com/file/scryfall-cards/normal/front/c/6/c6704458-6e9e-4795-a56d-25b68fbf9672.jpg?1627704159", "art_crop": "https://c1.scryfall.com/file/scryfall-cards/art_crop/front/c/6/c6704458-6e9e-4795-a56d-25b68fbf9672.jpg?1627704159"}, "reprint": false, "digital": false, "set_type": "expansion"}]}
\ No newline at end of file
diff --git a/web/tailwind.config.js b/web/tailwind.config.js
index 31c0c533..3457b7a6 100644
--- a/web/tailwind.config.js
+++ b/web/tailwind.config.js
@@ -1,4 +1,5 @@
 const defaultTheme = require('tailwindcss/defaultTheme')
+const plugin = require('tailwindcss/plugin')
 
 module.exports = {
   content: [
@@ -17,6 +18,14 @@ module.exports = {
       backgroundImage: {
         'world-trading': "url('/world-trading-background.webp')",
       },
+      typography: {
+        quoteless: {
+          css: {
+            'blockquote p:first-of-type::before': { content: 'none' },
+            'blockquote p:first-of-type::after': { content: 'none' },
+          },
+        },
+      },
     },
   },
   plugins: [
@@ -24,6 +33,22 @@ module.exports = {
     require('@tailwindcss/typography'),
     require('@tailwindcss/line-clamp'),
     require('daisyui'),
+    plugin(function ({ addUtilities }) {
+      addUtilities({
+        '.scrollbar-hide': {
+          /* IE and Edge */
+          '-ms-overflow-style': 'none',
+
+          /* Firefox */
+          'scrollbar-width': 'none',
+
+          /* Safari and Chrome */
+          '&::-webkit-scrollbar': {
+            display: 'none',
+          },
+        },
+      })
+    }),
   ],
   daisyui: {
     themes: [
diff --git a/yarn.lock b/yarn.lock
index 6fcdf53a..ffa8e6f0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2385,10 +2385,10 @@
   resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b"
   integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==
 
-"@next/env@12.1.2":
-  version "12.1.2"
-  resolved "https://registry.yarnpkg.com/@next/env/-/env-12.1.2.tgz#4b0f5fd448ac60b821d2486d2987948e3a099f03"
-  integrity sha512-A/P4ysmFScBFyu1ZV0Mr1Y89snyQhqGwsCrkEpK+itMF+y+pMqBoPVIyakUf4LXqGWJGiGFuIerihvSG70Ad8Q==
+"@next/env@12.2.2":
+  version "12.2.2"
+  resolved "https://registry.yarnpkg.com/@next/env/-/env-12.2.2.tgz#cc1a0a445bd254499e30f632968c03192455f4cc"
+  integrity sha512-BqDwE4gDl1F608TpnNxZqrCn6g48MBjvmWFEmeX5wEXDXh3IkAOw6ASKUgjT8H4OUePYFqghDFUss5ZhnbOUjw==
 
 "@next/eslint-plugin-next@12.1.6":
   version "12.1.6"
@@ -2397,65 +2397,70 @@
   dependencies:
     glob "7.1.7"
 
-"@next/swc-android-arm-eabi@12.1.2":
-  version "12.1.2"
-  resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.1.2.tgz#675e952d9032ac7bec02f3f413c17d33bbd90857"
-  integrity sha512-iwalfLBhYmCIlj09czFbovj1SmTycf0AGR8CB357wgmEN8xIuznIwSsCH87AhwQ9apfNtdeDhxvuKmhS9T3FqQ==
+"@next/swc-android-arm-eabi@12.2.2":
+  version "12.2.2"
+  resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.2.2.tgz#f6c4111e6371f73af6bf80c9accb3d96850a92cd"
+  integrity sha512-VHjuCHeq9qCprUZbsRxxM/VqSW8MmsUtqB5nEpGEgUNnQi/BTm/2aK8tl7R4D0twGKRh6g1AAeFuWtXzk9Z/vQ==
 
-"@next/swc-android-arm64@12.1.2":
-  version "12.1.2"
-  resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.1.2.tgz#d9710c50853235f258726b19a649df9c29a49682"
-  integrity sha512-ZoR0Vx7czJhTgRAcFbzTKQc2n2ChC036/uc6PbgYiI/LreEnfmsV/CiREP0pUVs5ndntOX8kBA3BSbh4zCO5tQ==
+"@next/swc-android-arm64@12.2.2":
+  version "12.2.2"
+  resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.2.2.tgz#b69de59c51e631a7600439e7a8993d6e82f3369e"
+  integrity sha512-v5EYzXUOSv0r9mO/2PX6mOcF53k8ndlu9yeFHVAWW1Dhw2jaJcvTRcCAwYYN8Q3tDg0nH3NbEltJDLKmcJOuVA==
 
-"@next/swc-darwin-arm64@12.1.2":
-  version "12.1.2"
-  resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.2.tgz#aadd21b711c82b3efa9b4ecf7665841259e1fa7e"
-  integrity sha512-VXv7lpqFjHwkK65CZHkjvBxlSBTG+l3O0Zl2zHniHj0xHzxJZvR8VFjV2zIMZCYSfVqeQ5yt2rjwuQ9zbpGtXQ==
+"@next/swc-darwin-arm64@12.2.2":
+  version "12.2.2"
+  resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.2.2.tgz#80157c91668eff95b72d052428c353eab0fc4c50"
+  integrity sha512-JCoGySHKGt+YBk7xRTFGx1QjrnCcwYxIo3yGepcOq64MoiocTM3yllQWeOAJU2/k9MH0+B5E9WUSme4rOCBbpA==
 
-"@next/swc-darwin-x64@12.1.2":
-  version "12.1.2"
-  resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.2.tgz#3b1a389828f5c88ecb828a6394692fdeaf175081"
-  integrity sha512-evXxJQnXEnU+heWyun7d0UV6bhBcmoiyFGR3O3v9qdhGbeXh+SXYVxRO69juuh6V7RWRdlb1KQ0rGUNa1k0XSw==
+"@next/swc-darwin-x64@12.2.2":
+  version "12.2.2"
+  resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.2.2.tgz#12be2f58e676fccff3d48a62921b9927ed295133"
+  integrity sha512-dztDtvfkhUqiqpXvrWVccfGhLe44yQ5tQ7B4tBfnsOR6vxzI9DNPHTlEOgRN9qDqTAcFyPxvg86mn4l8bB9Jcw==
 
-"@next/swc-linux-arm-gnueabihf@12.1.2":
-  version "12.1.2"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.2.tgz#db4371ca716bf94c94d4f6b001ac3c9d08d97d79"
-  integrity sha512-LJV/wo6R0Ot7Y/20bZs00aBG4J333RT6H/5Q2AROE4Hnx7cenSktSnfU6WCnJgzYLSIHdbLs549LcZMULuVquw==
+"@next/swc-freebsd-x64@12.2.2":
+  version "12.2.2"
+  resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.2.2.tgz#de1363431a49059f1efb8c0f86ce6a79c53b3a95"
+  integrity sha512-JUnXB+2xfxqsAvhFLPJpU1NeyDsvJrKoOjpV7g3Dxbno2Riu4tDKn3kKF886yleAuD/1qNTUCpqubTvbbT2VoA==
 
-"@next/swc-linux-arm64-gnu@12.1.2":
-  version "12.1.2"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.2.tgz#0e71db03b8b12ed315c8be7d15392ecefe562b7c"
-  integrity sha512-fjlYU1Y8kVjjRKyuyQBYLHPxjGOS2ox7U8TqAvtgKvd2PxqdsgW4sP+VDovRVPrZlGXNllKoJiqMO1OoR9fB6w==
+"@next/swc-linux-arm-gnueabihf@12.2.2":
+  version "12.2.2"
+  resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.2.2.tgz#d5b8e0d1bb55bbd9db4d2fec018217471dc8b9e6"
+  integrity sha512-XeYC/qqPLz58R4pjkb+x8sUUxuGLnx9QruC7/IGkK68yW4G17PHwKI/1njFYVfXTXUukpWjcfBuauWwxp9ke7Q==
 
-"@next/swc-linux-arm64-musl@12.1.2":
-  version "12.1.2"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.2.tgz#f1b055793da1c12167ed3b6e32aef8289721a1fb"
-  integrity sha512-Y1JRDMHqSjLObjyrD1hf6ePrJcOF/mkw+LbAzoNgrHL1dSuIAqcz3jYunJt8T7Yw48xSJy6LPSL9BclAHwEwOA==
+"@next/swc-linux-arm64-gnu@12.2.2":
+  version "12.2.2"
+  resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.2.2.tgz#3bc75984e1d5ec8f59eb53702cc382d8e1be2061"
+  integrity sha512-d6jT8xgfKYFkzR7J0OHo2D+kFvY/6W8qEo6/hmdrTt6AKAqxs//rbbcdoyn3YQq1x6FVUUd39zzpezZntg9Naw==
 
-"@next/swc-linux-x64-gnu@12.1.2":
-  version "12.1.2"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.2.tgz#69764ffaacb3b9b373897fff15d7dd871455efe2"
-  integrity sha512-5N4QSRT60ikQqCU8iHfYZzlhg6MFTLsKhMTARmhn8wLtZfN9VVyTFwZrJQWjV64dZc4JFeXDANGao8fm55y6bw==
+"@next/swc-linux-arm64-musl@12.2.2":
+  version "12.2.2"
+  resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.2.2.tgz#270db73e07a18d999f61e79a917943fa5bc1ef56"
+  integrity sha512-rIZRFxI9N/502auJT1i7coas0HTHUM+HaXMyJiCpnY8Rimbo0495ir24tzzHo3nQqJwcflcPTwEh/DV17sdv9A==
 
-"@next/swc-linux-x64-musl@12.1.2":
-  version "12.1.2"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.2.tgz#0ddaedb5ec578c01771f83be2046dafb2f70df91"
-  integrity sha512-b32F/xAgdYG4Pt0foFzhF+2uhvNxnEj7aJNp1R4EhZotdej2PzvFWcP/dGkc7MJl205pBz5oC3gHyILIIlW6XA==
+"@next/swc-linux-x64-gnu@12.2.2":
+  version "12.2.2"
+  resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.2.2.tgz#e6c72fa20478552e898c434f4d4c0c5e89d2ea78"
+  integrity sha512-ir1vNadlUDj7eQk15AvfhG5BjVizuCHks9uZwBfUgT5jyeDCeRvaDCo1+Q6+0CLOAnYDR/nqSCvBgzG2UdFh9A==
 
-"@next/swc-win32-arm64-msvc@12.1.2":
-  version "12.1.2"
-  resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.2.tgz#9e17ed56d5621f8c6961193da3a0b155cea511c9"
-  integrity sha512-hVOcGmWDeVwO00Aclopsj6MoYhfJl5zA4vjAai9KjgclQTFZa/DC0vQjgKAHHKGT5oMHgjiq/G7L6P1/UfwYnw==
+"@next/swc-linux-x64-musl@12.2.2":
+  version "12.2.2"
+  resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.2.2.tgz#b9ef9efe2c401839cdefa5e70402386aafdce15a"
+  integrity sha512-bte5n2GzLN3O8JdSFYWZzMgEgDHZmRz5wiispiiDssj4ik3l8E7wq/czNi8RmIF+ioj2sYVokUNa/ekLzrESWw==
 
-"@next/swc-win32-ia32-msvc@12.1.2":
-  version "12.1.2"
-  resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.2.tgz#ddd260cbe8bc4002fb54415b80baccf37f8db783"
-  integrity sha512-wnVDGIVz2pR3vIkyN6IE+1NvMSBrBj1jba11iR16m8TAPzZH/PrNsxr0a9N5VavEXXLcQpoUVvT+N7nflbRAHg==
+"@next/swc-win32-arm64-msvc@12.2.2":
+  version "12.2.2"
+  resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.2.2.tgz#18fa7ec7248da3a7926a0601d9ececc53ac83157"
+  integrity sha512-ZUGCmcDmdPVSAlwJ/aD+1F9lYW8vttseiv4n2+VCDv5JloxiX9aY32kYZaJJO7hmTLNrprvXkb4OvNuHdN22Jg==
 
-"@next/swc-win32-x64-msvc@12.1.2":
-  version "12.1.2"
-  resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.2.tgz#37412a314bcf4c6006a74e1ef9764048344f3848"
-  integrity sha512-MLNcurEpQp0+7OU9261f7PkN52xTGkfrt4IYTIXau7DO/aHj927oK6piIJdl9EOHdX/KN5W6qlyErj170PSHtw==
+"@next/swc-win32-ia32-msvc@12.2.2":
+  version "12.2.2"
+  resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.2.2.tgz#54936e84f4a219441d051940354da7cd3eafbb4f"
+  integrity sha512-v7ykeEDbr9eXiblGSZiEYYkWoig6sRhAbLKHUHQtk8vEWWVEqeXFcxmw6LRrKu5rCN1DY357UlYWToCGPQPCRA==
+
+"@next/swc-win32-x64-msvc@12.2.2":
+  version "12.2.2"
+  resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.2.2.tgz#7460be700a60d75816f01109400b51fe929d7e89"
+  integrity sha512-2D2iinWUL6xx8D9LYVZ5qi7FP6uLAoWymt8m8aaG2Ld/Ka8/k723fJfiklfuAcwOxfufPJI+nRbT5VcgHGzHAQ==
 
 "@nivo/annotations@0.74.0":
   version "0.74.0"
@@ -2837,6 +2842,13 @@
     "@svgr/plugin-jsx" "^6.2.1"
     "@svgr/plugin-svgo" "^6.2.0"
 
+"@swc/helpers@0.4.2":
+  version "0.4.2"
+  resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.2.tgz#ed1f6997ffbc22396665d9ba74e2a5c0a2d782f8"
+  integrity sha512-556Az0VX7WR6UdoTn4htt/l3zPQ7bsQWK+HqdG4swV7beUCxo/BqmvbOpUkTIm/9ih86LIf1qsUnywNL3obGHw==
+  dependencies:
+    tslib "^2.4.0"
+
 "@szmarczak/http-timer@^1.1.2":
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421"
@@ -4290,7 +4302,7 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001335:
   resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001344.tgz#8a1e7fdc4db9c2ec79a05e9fd68eb93a761888bb"
   integrity sha512-0ZFjnlCaXNOAYcV7i+TtdKBp0L/3XEU2MF/x6Du1lrh+SRX4IfzIVL4HNJg5pB2PmFb8rszIGyOvsZnqqRoc2g==
 
-caniuse-lite@^1.0.30001230, caniuse-lite@^1.0.30001283, caniuse-lite@^1.0.30001332:
+caniuse-lite@^1.0.30001230, caniuse-lite@^1.0.30001332:
   version "1.0.30001341"
   resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001341.tgz#59590c8ffa8b5939cf4161f00827b8873ad72498"
   integrity sha512-2SodVrFFtvGENGCv0ChVJIDQ0KPaS1cg7/qtfMaICgeMolDdo/Z2OD32F0Aq9yl6F4YFwGPBS5AaPqNYiW4PoA==
@@ -8320,29 +8332,31 @@ next-sitemap@^2.5.14:
     "@corex/deepmerge" "^2.6.148"
     minimist "^1.2.6"
 
-next@12.1.2:
-  version "12.1.2"
-  resolved "https://registry.yarnpkg.com/next/-/next-12.1.2.tgz#c5376a8ae17d3e404a2b691c01f94c8943306f29"
-  integrity sha512-JHPCsnFTBO0Z4SQxSYc611UA1WA+r/3y3Neg66AH5/gSO/oksfRnFw/zGX/FZ9+oOUHS9y3wJFawNpVYR2gJSQ==
+next@12.2.2:
+  version "12.2.2"
+  resolved "https://registry.yarnpkg.com/next/-/next-12.2.2.tgz#029bf5e4a18a891ca5d05b189b7cd983fd22c072"
+  integrity sha512-zAYFY45aBry/PlKONqtlloRFqU/We3zWYdn2NoGvDZkoYUYQSJC8WMcalS5C19MxbCZLUVCX7D7a6gTGgl2yLg==
   dependencies:
-    "@next/env" "12.1.2"
-    caniuse-lite "^1.0.30001283"
+    "@next/env" "12.2.2"
+    "@swc/helpers" "0.4.2"
+    caniuse-lite "^1.0.30001332"
     postcss "8.4.5"
-    styled-jsx "5.0.1"
-    use-subscription "1.5.1"
+    styled-jsx "5.0.2"
+    use-sync-external-store "1.1.0"
   optionalDependencies:
-    "@next/swc-android-arm-eabi" "12.1.2"
-    "@next/swc-android-arm64" "12.1.2"
-    "@next/swc-darwin-arm64" "12.1.2"
-    "@next/swc-darwin-x64" "12.1.2"
-    "@next/swc-linux-arm-gnueabihf" "12.1.2"
-    "@next/swc-linux-arm64-gnu" "12.1.2"
-    "@next/swc-linux-arm64-musl" "12.1.2"
-    "@next/swc-linux-x64-gnu" "12.1.2"
-    "@next/swc-linux-x64-musl" "12.1.2"
-    "@next/swc-win32-arm64-msvc" "12.1.2"
-    "@next/swc-win32-ia32-msvc" "12.1.2"
-    "@next/swc-win32-x64-msvc" "12.1.2"
+    "@next/swc-android-arm-eabi" "12.2.2"
+    "@next/swc-android-arm64" "12.2.2"
+    "@next/swc-darwin-arm64" "12.2.2"
+    "@next/swc-darwin-x64" "12.2.2"
+    "@next/swc-freebsd-x64" "12.2.2"
+    "@next/swc-linux-arm-gnueabihf" "12.2.2"
+    "@next/swc-linux-arm64-gnu" "12.2.2"
+    "@next/swc-linux-arm64-musl" "12.2.2"
+    "@next/swc-linux-x64-gnu" "12.2.2"
+    "@next/swc-linux-x64-musl" "12.2.2"
+    "@next/swc-win32-arm64-msvc" "12.2.2"
+    "@next/swc-win32-ia32-msvc" "12.2.2"
+    "@next/swc-win32-x64-msvc" "12.2.2"
 
 no-case@^3.0.4:
   version "3.0.4"
@@ -10892,10 +10906,10 @@ style-to-object@0.3.0, style-to-object@^0.3.0:
   dependencies:
     inline-style-parser "0.1.1"
 
-styled-jsx@5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.1.tgz#78fecbbad2bf95ce6cd981a08918ce4696f5fc80"
-  integrity sha512-+PIZ/6Uk40mphiQJJI1202b+/dYeTVd9ZnMPR80pgiWbjIwvN2zIp4r9et0BgqBuShh48I0gttPlAXA7WVvBxw==
+styled-jsx@5.0.2:
+  version "5.0.2"
+  resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.2.tgz#ff230fd593b737e9e68b630a694d460425478729"
+  integrity sha512-LqPQrbBh3egD57NBcHET4qcgshPks+yblyhPlH2GY8oaDgKs8SK4C3dBh3oSJjgzJ3G5t1SYEZGHkP+QEpX9EQ==
 
 stylehacks@^5.1.0:
   version "5.1.0"
@@ -11437,12 +11451,10 @@ use-latest@^1.2.1:
   dependencies:
     use-isomorphic-layout-effect "^1.1.1"
 
-use-subscription@1.5.1:
-  version "1.5.1"
-  resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1"
-  integrity sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA==
-  dependencies:
-    object-assign "^4.1.1"
+use-sync-external-store@1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.1.0.tgz#3343c3fe7f7e404db70f8c687adf5c1652d34e82"
+  integrity sha512-SEnieB2FPKEVne66NpXPd1Np4R1lTNKfjuy3XdIoPQKYBAFdzbzSZlSn1KJZUiihQLQC5Znot4SBz1EOTBwQAQ==
 
 util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
   version "1.0.2"