Merge "croupier: Add Resume Game button"
diff --git a/go/src/hearts/assets/AcrossArrowBlue.png b/go/src/hearts/assets/AcrossArrowBlue.png
new file mode 100644
index 0000000..f569f18
--- /dev/null
+++ b/go/src/hearts/assets/AcrossArrowBlue.png
Binary files differ
diff --git a/go/src/hearts/assets/AcrossArrowGray.png b/go/src/hearts/assets/AcrossArrowGray.png
new file mode 100644
index 0000000..de97530
--- /dev/null
+++ b/go/src/hearts/assets/AcrossArrowGray.png
Binary files differ
diff --git a/go/src/hearts/assets/LeftArrowBlue.png b/go/src/hearts/assets/LeftArrowBlue.png
new file mode 100644
index 0000000..f949609
--- /dev/null
+++ b/go/src/hearts/assets/LeftArrowBlue.png
Binary files differ
diff --git a/go/src/hearts/assets/LeftArrowGray.png b/go/src/hearts/assets/LeftArrowGray.png
new file mode 100644
index 0000000..dddac1a
--- /dev/null
+++ b/go/src/hearts/assets/LeftArrowGray.png
Binary files differ
diff --git a/go/src/hearts/assets/PassPressed.png b/go/src/hearts/assets/PassPressed.png
new file mode 100644
index 0000000..c5a5a28
--- /dev/null
+++ b/go/src/hearts/assets/PassPressed.png
Binary files differ
diff --git a/go/src/hearts/assets/PassUnpressed.png b/go/src/hearts/assets/PassUnpressed.png
new file mode 100644
index 0000000..a90664e
--- /dev/null
+++ b/go/src/hearts/assets/PassUnpressed.png
Binary files differ
diff --git a/go/src/hearts/assets/QuitPressed.png b/go/src/hearts/assets/QuitPressed.png
index f42be9b..2f45344 100644
--- a/go/src/hearts/assets/QuitPressed.png
+++ b/go/src/hearts/assets/QuitPressed.png
Binary files differ
diff --git a/go/src/hearts/assets/QuitUnpressed.png b/go/src/hearts/assets/QuitUnpressed.png
index 47deba6..6d62380 100644
--- a/go/src/hearts/assets/QuitUnpressed.png
+++ b/go/src/hearts/assets/QuitUnpressed.png
Binary files differ
diff --git a/go/src/hearts/assets/RightArrowBlue.png b/go/src/hearts/assets/RightArrowBlue.png
new file mode 100644
index 0000000..b147d7e
--- /dev/null
+++ b/go/src/hearts/assets/RightArrowBlue.png
Binary files differ
diff --git a/go/src/hearts/assets/RightArrowGray.png b/go/src/hearts/assets/RightArrowGray.png
new file mode 100644
index 0000000..59866a0
--- /dev/null
+++ b/go/src/hearts/assets/RightArrowGray.png
Binary files differ
diff --git a/go/src/hearts/assets/SitSpotPressed.png b/go/src/hearts/assets/SitSpotPressed.png
index b4ada5f..c11470e 100644
--- a/go/src/hearts/assets/SitSpotPressed.png
+++ b/go/src/hearts/assets/SitSpotPressed.png
Binary files differ
diff --git a/go/src/hearts/assets/SitSpotUnpressed.png b/go/src/hearts/assets/SitSpotUnpressed.png
index 0991f49..8943bb6 100644
--- a/go/src/hearts/assets/SitSpotUnpressed.png
+++ b/go/src/hearts/assets/SitSpotUnpressed.png
Binary files differ
diff --git a/go/src/hearts/assets/StartGray.png b/go/src/hearts/assets/StartGray.png
index aced4e1..89e1dee 100644
--- a/go/src/hearts/assets/StartGray.png
+++ b/go/src/hearts/assets/StartGray.png
Binary files differ
diff --git a/go/src/hearts/assets/WatchSpotPressed.png b/go/src/hearts/assets/WatchSpotPressed.png
index 6060786..c51cc49 100644
--- a/go/src/hearts/assets/WatchSpotPressed.png
+++ b/go/src/hearts/assets/WatchSpotPressed.png
Binary files differ
diff --git a/go/src/hearts/assets/WatchSpotUnpressed.png b/go/src/hearts/assets/WatchSpotUnpressed.png
index 37b446c..67413e5 100644
--- a/go/src/hearts/assets/WatchSpotUnpressed.png
+++ b/go/src/hearts/assets/WatchSpotUnpressed.png
Binary files differ
diff --git a/go/src/hearts/img/reposition/reposition.go b/go/src/hearts/img/reposition/reposition.go
index 4f32f36..0e7a6e5 100644
--- a/go/src/hearts/img/reposition/reposition.go
+++ b/go/src/hearts/img/reposition/reposition.go
@@ -179,19 +179,14 @@
 }
 
 // Animation for the 'pass' action, when app is in the hand view
-func AnimateHandCardPass(ch chan bool, animImages []*staticimg.StaticImg, animCards []*card.Card, u *uistate.UIState) {
-	for _, i := range animImages {
+func AnimateHandCardPass(ch chan bool, animImages []*staticimg.StaticImg, u *uistate.UIState) {
+	for counter, i := range animImages {
 		dims := i.GetDimensions()
 		to := coords.MakeVec(i.GetCurrent().X, i.GetCurrent().Y-u.WindowSize.Y)
-		AnimateImageNoChannel(i, to, dims, u)
-	}
-	for i, c := range animCards {
-		dims := c.GetDimensions()
-		to := coords.MakeVec(c.GetCurrent().X, c.GetCurrent().Y-u.WindowSize.Y)
-		if i < len(animCards)-1 {
-			animateCardNoChannel(c, to, dims, u)
+		if counter < len(animImages)-1 {
+			AnimateImageNoChannel(i, to, dims, u)
 		} else {
-			animateCardMovement(ch, c, to, dims, u)
+			animateImageMovement(ch, i, to, dims, u)
 		}
 	}
 }
@@ -211,18 +206,21 @@
 // Animation to bring in the take slot
 func AnimateInTake(u *uistate.UIState) {
 	imgs := append(u.Other, u.DropTargets...)
-	passedCards := u.CurTable.GetPlayers()[u.CurPlayerIndex].GetPassedTo()
 	for _, i := range imgs {
 		dims := i.GetDimensions()
 		to := coords.MakeVec(i.GetCurrent().X, i.GetCurrent().Y+u.WindowSize.Y)
 		AnimateImageNoChannel(i, to, dims, u)
 	}
-	if !u.SequentialPhases || u.CurTable.AllDonePassing() {
-		for _, c := range passedCards {
-			dims := c.GetDimensions()
-			to := coords.MakeVec(c.GetCurrent().X, c.GetCurrent().Y+u.WindowSize.Y)
-			animateCardNoChannel(c, to, dims, u)
-		}
+}
+
+// Animation to bring in the pass slot
+func AnimateInPass(u *uistate.UIState) {
+	imgs := append(u.Other, u.DropTargets...)
+	imgs = append(imgs, u.Buttons["pass"])
+	for _, i := range imgs {
+		dims := i.GetDimensions()
+		to := coords.MakeVec(i.GetCurrent().X, i.GetCurrent().Y+u.WindowSize.Y)
+		AnimateImageNoChannel(i, to, dims, u)
 	}
 }
 
@@ -238,13 +236,16 @@
 		BringNodeToFront(img.GetNode(), u)
 	}
 	imgs := []*staticimg.StaticImg{u.BackgroundImgs[0], u.DropTargets[0]}
-	for _, i := range imgs {
+	for counter, i := range imgs {
 		dims := i.GetDimensions()
 		to := coords.MakeVec(i.GetCurrent().X, i.GetCurrent().Y-u.WindowSize.Y)
-		AnimateImageNoChannel(i, to, dims, u)
+		if counter == len(imgs)-1 {
+			animateImageMovement(ch, i, to, dims, u)
+		} else {
+			AnimateImageNoChannel(i, to, dims, u)
+		}
+
 	}
-	to := coords.MakeVec(animCard.GetCurrent().X, animCard.GetCurrent().Y-u.WindowSize.Y)
-	animateCardMovement(ch, animCard, to, animCard.GetDimensions(), u)
 }
 
 // Animation to bring in the play slot when app is in the hand view and it is the player's turn
@@ -281,17 +282,10 @@
 	topOfBanner := u.WindowSize.Y - 4*u.CardDim.Y - 5*u.Padding - u.BottomPadding - 40
 	tableImgs := make([]*staticimg.StaticImg, 0)
 	bannerImgs := make([]*staticimg.StaticImg, 0)
-	cards := make([]*card.Card, 0)
 	bannerImgs = append(bannerImgs, u.Other...)
 	bannerImgs = append(bannerImgs, u.Buttons["toggleSplit"])
 	tableImgs = append(tableImgs, u.DropTargets...)
 	tableImgs = append(tableImgs, u.BackgroundImgs[:u.NumPlayers]...)
-	cards = append(cards, u.TableCards...)
-	for _, card := range cards {
-		from := card.GetCurrent()
-		to := coords.MakeVec(from.X, from.Y+topOfBanner)
-		animateCardNoChannel(card, to, card.GetDimensions(), u)
-	}
 	for _, img := range tableImgs {
 		from := img.GetCurrent()
 		to := coords.MakeVec(from.X, from.Y+topOfBanner)
@@ -326,17 +320,10 @@
 	topOfBanner := u.WindowSize.Y - 4*u.CardDim.Y - 5*u.Padding - u.BottomPadding - 40
 	tableImgs := make([]*staticimg.StaticImg, 0)
 	bannerImgs := make([]*staticimg.StaticImg, 0)
-	cards := make([]*card.Card, 0)
 	bannerImgs = append(bannerImgs, u.Other...)
 	bannerImgs = append(bannerImgs, u.Buttons["toggleSplit"])
 	tableImgs = append(tableImgs, u.DropTargets...)
 	tableImgs = append(tableImgs, u.BackgroundImgs[:u.NumPlayers]...)
-	cards = append(cards, u.TableCards...)
-	for _, card := range cards {
-		from := card.GetCurrent()
-		to := coords.MakeVec(from.X, from.Y-topOfBanner)
-		animateCardNoChannel(card, to, card.GetDimensions(), u)
-	}
 	for _, img := range tableImgs {
 		from := img.GetCurrent()
 		to := coords.MakeVec(from.X, from.Y-topOfBanner)
@@ -417,8 +404,16 @@
 			newVec := curXY.PlusVec(XYStep)
 			dimVec := curDim.PlusVec(dimStep)
 			animImage.Move(newVec, dimVec, eng)
+			card := animImage.GetCardHere()
+			if card != nil {
+				card.Move(newVec, dimVec, eng)
+			}
 		} else if iteration == animationFrameCount {
 			animImage.Move(endPos, endDim, eng)
+			card := animImage.GetCardHere()
+			if card != nil {
+				card.Move(endPos, endDim, eng)
+			}
 			c <- true
 		}
 	})
@@ -439,8 +434,16 @@
 			newVec := curXY.PlusVec(XYStep)
 			dimVec := curDim.PlusVec(dimStep)
 			animImage.Move(newVec, dimVec, eng)
+			card := animImage.GetCardHere()
+			if card != nil {
+				card.Move(newVec, dimVec, eng)
+			}
 		} else if iteration == animationFrameCount {
 			animImage.Move(endPos, endDim, eng)
+			card := animImage.GetCardHere()
+			if card != nil {
+				card.Move(endPos, endDim, eng)
+			}
 		}
 	})
 }
diff --git a/go/src/hearts/img/texture/texture.go b/go/src/hearts/img/texture/texture.go
index fd96e76..aacf862 100644
--- a/go/src/hearts/img/texture/texture.go
+++ b/go/src/hearts/img/texture/texture.go
@@ -275,6 +275,8 @@
 		"NewRoundPressed.png", "NewRoundUnpressed.png", "JoinGamePressed.png", "JoinGameUnpressed.png", "Period.png",
 		"SitSpotPressed.png", "SitSpotUnpressed.png", "WatchSpotPressed.png", "WatchSpotUnpressed.png", "StartBlue.png", "StartGray.png",
 		"StartBluePressed.png", "Restart.png", "Visibility.png", "VisibilityOff.png", "QuitPressed.png", "QuitUnpressed.png",
+		"PassPressed.png", "PassUnpressed.png", "RightArrowBlue.png", "LeftArrowBlue.png", "AcrossArrowBlue.png", "RightArrowGray.png",
+		"LeftArrowGray.png", "AcrossArrowGray.png",
 	}
 	for _, f := range boundedImgs {
 		a, err := asset.Open(f)
diff --git a/go/src/hearts/img/uistate/uistate.go b/go/src/hearts/img/uistate/uistate.go
index bb5c898..cbdb649 100644
--- a/go/src/hearts/img/uistate/uistate.go
+++ b/go/src/hearts/img/uistate/uistate.go
@@ -134,7 +134,7 @@
 		Padding:          float32(5),
 		CurView:          None,
 		Done:             false,
-		Debug:            false,
+		Debug:            true,
 		SequentialPhases: true,
 		SwitchingViews:   false,
 		UserData:         make(map[int]map[string]interface{}),
diff --git a/go/src/hearts/img/view/view.go b/go/src/hearts/img/view/view.go
index 29d26bd..fcd4814 100644
--- a/go/src/hearts/img/view/view.go
+++ b/go/src/hearts/img/view/view.go
@@ -28,11 +28,13 @@
 
 // Arrange view: For seating players
 func LoadArrangeView(u *uistate.UIState) {
-	u.CurView = uistate.Arrange
-	<-time.After(1 * time.Second)
-	reposition.ResetAnims(u)
+	if len(u.AnimChans) > 0 {
+		reposition.ResetAnims(u)
+		<-time.After(time.Second)
+	}
 	resetImgs(u)
 	resetScene(u)
+	u.CurView = uistate.Arrange
 	addHeader(u)
 	watchImg := u.Texs["WatchSpotUnpressed.png"]
 	watchAlt := u.Texs["WatchSpotPressed.png"]
@@ -58,19 +60,26 @@
 	quitPos := coords.MakeVec(u.Padding, u.TopPadding+10)
 	u.Buttons["exit"] = texture.MakeImgWithAlt(quitImg, quitAlt, quitPos, quitDim, true, u)
 	if u.IsOwner {
-		startImg := u.Texs["StartGray.png"]
-		startAlt := u.Texs["StartBlue.png"]
+		startImg := u.Texs["StartBlue.png"]
+		startAlt := u.Texs["StartBluePressed.png"]
 		startDim := coords.MakeVec(2*u.CardDim.X, u.CardDim.Y)
 		startPos := u.WindowSize.MinusVec(startDim).Minus(u.BottomPadding)
-		display := !u.CurTable.AllReadyForNewRound()
-		u.Buttons["start"] = texture.MakeImgWithAlt(startImg, startAlt, startPos, startDim, display, u)
+		display := u.CurTable.AllReadyForNewRound()
+		u.Buttons["start"] = texture.MakeImgWithAlt(startImg, startAlt, startPos, startDim, true, u)
+		var emptyTex sprite.SubTex
+		if !display {
+			u.Eng.SetSubTex(u.Buttons["start"].GetNode(), emptyTex)
+		}
 	}
 }
 
 // Waiting view: Displays the word "Waiting". To be displayed when players are waiting for a new round to be dealt
 // TODO(emshack): Integrate this with Arrange view and Score view so that a separate screen is not necessary
 func LoadWaitingView(u *uistate.UIState) {
-	reposition.ResetAnims(u)
+	if len(u.AnimChans) > 0 {
+		reposition.ResetAnims(u)
+		<-time.After(time.Second)
+	}
 	resetImgs(u)
 	resetScene(u)
 	center := u.WindowSize.DividedBy(2)
@@ -84,10 +93,13 @@
 
 // Discovery view: Displays a menu of possible games to join
 func LoadDiscoveryView(u *uistate.UIState) {
-	u.CurView = uistate.Discovery
-	reposition.ResetAnims(u)
+	if len(u.AnimChans) > 0 {
+		reposition.ResetAnims(u)
+		<-time.After(time.Second)
+	}
 	resetImgs(u)
 	resetScene(u)
+	u.CurView = uistate.Discovery
 	newGameImg := u.Texs["NewGameUnpressed.png"]
 	newGameAlt := u.Texs["NewGamePressed.png"]
 	newGameDim := coords.MakeVec(2*u.CardDim.X, u.CardDim.Y)
@@ -128,10 +140,13 @@
 
 // Table View: Displays the table. Intended for public devices
 func LoadTableView(u *uistate.UIState) {
-	u.CurView = uistate.Table
-	reposition.ResetAnims(u)
+	if len(u.AnimChans) > 0 {
+		reposition.ResetAnims(u)
+		<-time.After(time.Second)
+	}
 	resetImgs(u)
 	resetScene(u)
+	u.CurView = uistate.Table
 	scaler := float32(6)
 	maxWidth := 4 * u.TableCardDim.X
 	// adding four drop targets for trick
@@ -354,7 +369,6 @@
 }
 
 // Decides which view of the player's hand to load based on what steps of the round they have completed
-// Likely just for debugging
 func LoadPassOrTakeOrPlay(u *uistate.UIState) {
 	p := u.CurTable.GetPlayers()[u.CurPlayerIndex]
 	if p.GetDoneTaking() || u.CurTable.GetDir() == direction.None {
@@ -368,10 +382,13 @@
 
 // Score View: Shows current player standings at the end of every round, including the end of the game
 func LoadScoreView(roundScores, winners []int, u *uistate.UIState) {
-	u.CurView = uistate.Score
-	reposition.ResetAnims(u)
+	if len(u.AnimChans) > 0 {
+		reposition.ResetAnims(u)
+		<-time.After(time.Second)
+	}
 	resetImgs(u)
 	resetScene(u)
+	u.CurView = uistate.Score
 	addHeader(u)
 	addScoreViewHeaderText(u)
 	addPlayerScores(roundScores, u)
@@ -380,25 +397,32 @@
 
 // Pass View: Shows player's hand and allows them to pass cards
 func LoadPassView(u *uistate.UIState) {
-	u.CurView = uistate.Pass
-	reposition.ResetAnims(u)
+	if len(u.AnimChans) > 0 {
+		reposition.ResetAnims(u)
+		<-time.After(time.Second)
+	}
 	resetImgs(u)
 	resetScene(u)
+	u.CurView = uistate.Pass
 	addHeader(u)
 	addGrayPassBar(u)
-	addPassDrops(u)
+	//addPassDrops(u)
 	addHand(u)
 	if u.Debug {
 		addDebugBar(u)
 	}
+	reposition.AnimateInPass(u)
 }
 
 // Take View: Shows player's hand and allows them to take the cards that have been passed to them
 func LoadTakeView(u *uistate.UIState) {
-	u.CurView = uistate.Take
-	reposition.ResetAnims(u)
+	if len(u.AnimChans) > 0 {
+		reposition.ResetAnims(u)
+		<-time.After(time.Second)
+	}
 	resetImgs(u)
 	resetScene(u)
+	u.CurView = uistate.Take
 	addHeader(u)
 	addGrayTakeBar(u)
 	addHand(u)
@@ -412,11 +436,13 @@
 
 // Play View: Shows player's hand and allows them to play cards
 func LoadPlayView(u *uistate.UIState) {
-	u.CurView = uistate.Play
-	fmt.Println("In play view")
-	reposition.ResetAnims(u)
+	if len(u.AnimChans) > 0 {
+		reposition.ResetAnims(u)
+		<-time.After(time.Second)
+	}
 	resetImgs(u)
 	resetScene(u)
+	u.CurView = uistate.Play
 	addPlaySlot(u)
 	addHand(u)
 	addPlayHeader(getTurnText(u), false, u)
@@ -437,11 +463,13 @@
 }
 
 func LoadSplitView(reloading bool, u *uistate.UIState) {
-	u.CurView = uistate.Split
-	fmt.Println("In split view")
-	reposition.ResetAnims(u)
+	if len(u.AnimChans) > 0 {
+		reposition.ResetAnims(u)
+		<-time.After(time.Second)
+	}
 	resetImgs(u)
 	resetScene(u)
+	u.CurView = uistate.Split
 	addPlayHeader(getTurnText(u), !reloading, u)
 	addSplitViewPlayerIcons(!reloading, u)
 	SetNumTricksHand(u)
@@ -536,8 +564,8 @@
 		dropTargetY -= topOfBanner
 	}
 	dropTargetPos := coords.MakeVec(dropTargetX, dropTargetY)
-	u.DropTargets = append(u.DropTargets,
-		texture.MakeImgWithAlt(dropTargetImage, dropTargetAlt, dropTargetPos, dropTargetDimensions, true, u))
+	d := texture.MakeImgWithAlt(dropTargetImage, dropTargetAlt, dropTargetPos, dropTargetDimensions, true, u)
+	u.DropTargets = append(u.DropTargets, d)
 	// first player icon
 	playerIconImage := uistate.GetAvatar(u.CurPlayerIndex, u)
 	u.BackgroundImgs = append(u.BackgroundImgs,
@@ -548,6 +576,7 @@
 		texture.PopulateCardImage(dropCard, u)
 		dropCard.SetInitial(dropTargetPos)
 		dropCard.Move(dropTargetPos, dropTargetDimensions, u.Eng)
+		d.SetCardHere(dropCard)
 		u.TableCards = append(u.TableCards, dropCard)
 	}
 	// second drop target
@@ -557,8 +586,8 @@
 	}
 	dropTargetX = splitWindowSize.X/2 - 3*u.CardDim.X/2 - u.Padding
 	dropTargetPos = coords.MakeVec(dropTargetX, dropTargetY)
-	u.DropTargets = append(u.DropTargets,
-		texture.MakeImgWithAlt(dropTargetImage, dropTargetAlt, dropTargetPos, dropTargetDimensions, true, u))
+	d = texture.MakeImgWithAlt(dropTargetImage, dropTargetAlt, dropTargetPos, dropTargetDimensions, true, u)
+	u.DropTargets = append(u.DropTargets, d)
 	// second player icon
 	playerIconImage = uistate.GetAvatar((u.CurPlayerIndex+1)%u.NumPlayers, u)
 	u.BackgroundImgs = append(u.BackgroundImgs,
@@ -569,6 +598,7 @@
 		texture.PopulateCardImage(dropCard, u)
 		dropCard.SetInitial(dropTargetPos)
 		dropCard.Move(dropTargetPos, dropTargetDimensions, u.Eng)
+		d.SetCardHere(dropCard)
 		u.TableCards = append(u.TableCards, dropCard)
 	}
 	// third drop target
@@ -578,8 +608,8 @@
 		dropTargetY -= topOfBanner
 	}
 	dropTargetPos = coords.MakeVec(dropTargetX, dropTargetY)
-	u.DropTargets = append(u.DropTargets,
-		texture.MakeImgWithAlt(dropTargetImage, dropTargetAlt, dropTargetPos, dropTargetDimensions, true, u))
+	d = texture.MakeImgWithAlt(dropTargetImage, dropTargetAlt, dropTargetPos, dropTargetDimensions, true, u)
+	u.DropTargets = append(u.DropTargets, d)
 	// third player icon
 	playerIconImage = uistate.GetAvatar((u.CurPlayerIndex+2)%u.NumPlayers, u)
 	u.BackgroundImgs = append(u.BackgroundImgs,
@@ -590,6 +620,7 @@
 		texture.PopulateCardImage(dropCard, u)
 		dropCard.SetInitial(dropTargetPos)
 		dropCard.Move(dropTargetPos, dropTargetDimensions, u.Eng)
+		d.SetCardHere(dropCard)
 		u.TableCards = append(u.TableCards, dropCard)
 	}
 	// fourth drop target
@@ -599,8 +630,8 @@
 	}
 	dropTargetX = splitWindowSize.X/2 + u.CardDim.X/2 + u.Padding
 	dropTargetPos = coords.MakeVec(dropTargetX, dropTargetY)
-	u.DropTargets = append(u.DropTargets,
-		texture.MakeImgWithAlt(dropTargetImage, dropTargetAlt, dropTargetPos, dropTargetDimensions, true, u))
+	d = texture.MakeImgWithAlt(dropTargetImage, dropTargetAlt, dropTargetPos, dropTargetDimensions, true, u)
+	u.DropTargets = append(u.DropTargets, d)
 	// fourth player icon
 	playerIconImage = uistate.GetAvatar((u.CurPlayerIndex+3)%u.NumPlayers, u)
 	u.BackgroundImgs = append(u.BackgroundImgs,
@@ -611,6 +642,7 @@
 		texture.PopulateCardImage(dropCard, u)
 		dropCard.SetInitial(dropTargetPos)
 		dropCard.Move(dropTargetPos, dropTargetDimensions, u.Eng)
+		d.SetCardHere(dropCard)
 		u.TableCards = append(u.TableCards, dropCard)
 	}
 }
@@ -688,28 +720,61 @@
 	// adding gray bar
 	grayBarImg := u.Texs["RoundedRectangle-Gray.png"]
 	blueBarImg := u.Texs["RoundedRectangle-LBlue.png"]
-	grayBarDim := u.WindowSize.Minus(4 * u.BottomPadding)
-	grayBarPos := coords.MakeVec(2*u.BottomPadding, 40-grayBarDim.Y+u.TopPadding)
+	topOfHand := u.WindowSize.Y - 5*(u.CardDim.Y+u.Padding) - (2 * u.Padding / 5) - u.BottomPadding
+	grayBarDim := coords.MakeVec(u.WindowSize.X-4*u.BottomPadding, topOfHand+u.CardDim.Y)
+	grayBarPos := coords.MakeVec(2*u.BottomPadding, -u.WindowSize.Y-20)
 	u.Other = append(u.Other,
 		texture.MakeImgWithAlt(grayBarImg, blueBarImg, grayBarPos, grayBarDim, true, u))
 	// adding name
 	var receivingPlayer int
+	var arrowImg sprite.SubTex
+	var arrowAlt sprite.SubTex
 	switch u.CurTable.GetDir() {
 	case direction.Right:
 		receivingPlayer = (u.CurPlayerIndex + 3) % u.NumPlayers
+		arrowImg = u.Texs["RightArrowGray.png"]
+		arrowAlt = u.Texs["RightArrowBlue.png"]
 	case direction.Left:
 		receivingPlayer = (u.CurPlayerIndex + 1) % u.NumPlayers
+		arrowImg = u.Texs["LeftArrowGray.png"]
+		arrowAlt = u.Texs["LeftArrowBlue.png"]
 	case direction.Across:
 		receivingPlayer = (u.CurPlayerIndex + 2) % u.NumPlayers
+		arrowImg = u.Texs["AcrossArrowGray.png"]
+		arrowAlt = u.Texs["AcrossArrowBlue.png"]
 	}
+	arrowDim := u.CardDim
 	name := uistate.GetName(receivingPlayer, u)
 	color := "Gray"
 	altColor := "LBlue"
-	center := coords.MakeVec(u.WindowSize.X/2, u.TopPadding+5)
+	center := coords.MakeVec(u.WindowSize.X/2-arrowDim.X/2, 20-u.WindowSize.Y)
 	scaler := float32(3)
-	maxWidth := grayBarDim.X - 2*u.Padding
-	nameImgs := texture.MakeStringImgCenterAlign(name, color, altColor, true, center, scaler, maxWidth, u)
+	maxWidth := grayBarDim.X - 3*u.Padding - arrowDim.X
+	nameImgs := texture.MakeStringImgCenterAlign(fmt.Sprintf("Pass to %s", name), color, altColor, true, center, scaler, maxWidth, u)
 	u.Other = append(u.Other, nameImgs...)
+	imgBeforeArrow := u.Other[len(u.Other)-1]
+	ibaDim := imgBeforeArrow.GetDimensions()
+	ibaPos := imgBeforeArrow.GetCurrent()
+	arrowPos := coords.MakeVec(ibaPos.X+ibaDim.X+u.Padding, ibaPos.Y+ibaDim.Y/2-arrowDim.Y/2)
+	u.Other = append(u.Other,
+		texture.MakeImgWithAlt(arrowImg, arrowAlt, arrowPos, arrowDim, true, u))
+	numDrops := 3
+	dropXStart := (u.WindowSize.X - (float32(numDrops)*u.CardDim.X + (float32(numDrops)-1)*u.Padding)) / 2
+	dropImg := u.Texs["trickDrop.png"]
+	for i := 0; i < numDrops; i++ {
+		dropX := dropXStart + float32(i)*(u.Padding+u.CardDim.X)
+		dropPos := coords.MakeVec(dropX, topOfHand-u.Padding-u.WindowSize.Y-20)
+		d := texture.MakeImgWithoutAlt(dropImg, dropPos, u.CardDim, u)
+		u.DropTargets = append(u.DropTargets, d)
+	}
+	passImg := u.Texs["PassUnpressed.png"]
+	passAlt := u.Texs["PassPressed.png"]
+	passDim := coords.MakeVec(2*u.CardDim.X, 2*u.CardDim.Y/3)
+	passPos := coords.MakeVec((u.WindowSize.X-passDim.X)/2, topOfHand-2*u.Padding-u.WindowSize.Y-20-passDim.Y)
+	b := texture.MakeImgWithAlt(passImg, passAlt, passPos, passDim, true, u)
+	var emptyTex sprite.SubTex
+	u.Eng.SetSubTex(b.GetNode(), emptyTex)
+	u.Buttons["pass"] = b
 }
 
 func addGrayTakeBar(u *uistate.UIState) {
diff --git a/go/src/hearts/sync/client.go b/go/src/hearts/sync/client.go
index 63f57fe..63d6442 100644
--- a/go/src/hearts/sync/client.go
+++ b/go/src/hearts/sync/client.go
@@ -95,7 +95,7 @@
 }
 
 // Joins gamelog syncgroup
-func JoinLogSyncgroup(ch chan bool, logName string, u *uistate.UIState) {
+func JoinLogSyncgroup(logName string, u *uistate.UIState) bool {
 	fmt.Println("Joining gamelog syncgroup")
 	u.IsOwner = false
 	app := u.Service.App(AppName)
@@ -105,13 +105,13 @@
 	_, err := logSg.Join(u.Ctx, myInfoJoiner)
 	if err != nil {
 		fmt.Println("SYNCGROUP JOIN ERROR: ", err)
-		ch <- false
+		return false
 	} else {
 		fmt.Println("Syncgroup joined")
 		if u.LogSG != logName {
 			resetGame(logName, false, u)
 		}
-		ch <- true
+		return true
 	}
 }
 
diff --git a/go/src/hearts/sync/server.go b/go/src/hearts/sync/server.go
index 03887be..38ed5f4 100644
--- a/go/src/hearts/sync/server.go
+++ b/go/src/hearts/sync/server.go
@@ -116,7 +116,7 @@
 }
 
 // Creates a new gamelog syncgroup
-func CreateLogSyncgroup(ch chan string, u *uistate.UIState) {
+func CreateLogSyncgroup(u *uistate.UIState) (string, string) {
 	fmt.Println("Creating Log Syncgroup")
 	u.IsOwner = true
 	// Generate random gameID information to advertise this game
@@ -131,7 +131,6 @@
 	if err != nil {
 		fmt.Println("WE HAVE A HUGE PROBLEM:", err)
 	}
-	ch <- string(value)
 	// Create gamelog syncgroup
 	logSGName := fmt.Sprintf("%s/croupier/%s/%%%%sync/gaming-%d", MountPoint, SBName, gameID)
 	allAccess := access.AccessList{In: []security.BlessingPattern{"..."}}
@@ -163,21 +162,21 @@
 		_, err2 := logSG.Join(u.Ctx, myInfoCreator)
 		if err2 != nil {
 			fmt.Println("SYNCGROUP JOIN ERROR: ", err2)
-			ch <- ""
+			return string(value), ""
 		} else {
-			ch <- logSGName
+			return string(value), logSGName
 		}
 	} else {
 		fmt.Println("Syncgroup created")
 		if logSGName != u.LogSG {
 			resetGame(logSGName, true, u)
 		}
-		ch <- logSGName
+		return string(value), logSGName
 	}
 }
 
 // Creates a new user settings syncgroup
-func CreateSettingsSyncgroup(ch chan string, u *uistate.UIState) {
+func CreateSettingsSyncgroup(u *uistate.UIState) string {
 	fmt.Println("Creating Settings Syncgroup")
 	allAccess := access.AccessList{In: []security.BlessingPattern{"..."}}
 	permissions := access.Permissions{
@@ -209,13 +208,13 @@
 		_, err2 := settingsSG.Join(u.Ctx, myInfoCreator)
 		if err2 != nil {
 			fmt.Println("SYNCGROUP JOIN ERROR: ", err2)
-			ch <- ""
+			return ""
 		} else {
-			ch <- settingsSGName
+			return settingsSGName
 		}
 	} else {
 		fmt.Println("Syncgroup created")
-		ch <- settingsSGName
+		return settingsSGName
 	}
 }
 
diff --git a/go/src/hearts/sync/util.go b/go/src/hearts/sync/util.go
index 8c6b586..b1da39f 100644
--- a/go/src/hearts/sync/util.go
+++ b/go/src/hearts/sync/util.go
@@ -8,12 +8,12 @@
 
 const (
 	// switch back to my mountpoint with the following code:
-	MountPoint = "users/emshack@google.com"
-	//MountPoint        = "/192.168.86.254:8101"
-	UserID            = 2222
+	//MountPoint = "users/emshack@google.com"
+	MountPoint        = "/192.168.86.254:8101"
+	UserID            = 1111
 	UserColor         = 16777215
-	UserAvatar        = "player1.jpeg"
-	UserName          = "Bob"
+	UserAvatar        = "player0.jpeg"
+	UserName          = "Alice"
 	SBName            = "syncbase"
 	AppName           = "app"
 	DbName            = "db"
diff --git a/go/src/hearts/sync/watch.go b/go/src/hearts/sync/watch.go
index 5e489f7..e458cf4 100644
--- a/go/src/hearts/sync/watch.go
+++ b/go/src/hearts/sync/watch.go
@@ -362,12 +362,8 @@
 	if u.CurTable.AllReadyForNewRound() && u.IsOwner {
 		if u.CurView == uistate.Arrange {
 			b := u.Buttons["start"]
-			if !b.GetDisplayingImage() {
-				b.SetImage(b.GetAlt())
-				b.SetAlt(u.Texs["StartBluePressed.png"])
-				u.Eng.SetSubTex(b.GetNode(), b.GetImage())
-				b.SetDisplayingImage(true)
-			}
+			u.Eng.SetSubTex(b.GetNode(), b.GetImage())
+			b.SetDisplayingImage(true)
 			if u.SGChan != nil {
 				u.SGChan <- true
 				u.SGChan = nil
diff --git a/go/src/hearts/touchhandler/touchhandler.go b/go/src/hearts/touchhandler/touchhandler.go
index f2cc3aa..f66ae14 100644
--- a/go/src/hearts/touchhandler/touchhandler.go
+++ b/go/src/hearts/touchhandler/touchhandler.go
@@ -14,7 +14,6 @@
 	"golang.org/x/mobile/event/touch"
 	"golang.org/x/mobile/exp/sprite"
 
-	"hearts/img/coords"
 	"hearts/img/reposition"
 	"hearts/img/staticimg"
 	"hearts/img/uistate"
@@ -51,9 +50,7 @@
 	case uistate.Table:
 		switch t.Type {
 		case touch.TypeBegin:
-			if u.Debug {
-				beginClickTable(t, u)
-			}
+			beginClickTable(t, u)
 		}
 	case uistate.Pass:
 		switch t.Type {
@@ -138,13 +135,8 @@
 	pressed := unpressButtons(u)
 	for _, button := range pressed {
 		if button == u.Buttons["newGame"] {
-			logCh := make(chan string)
-			settingsCh := make(chan string)
-			go sync.CreateLogSyncgroup(logCh, u)
-			go sync.CreateSettingsSyncgroup(settingsCh, u)
-			gameStartData := <-logCh
-			logName := <-logCh
-			settingsName := <-settingsCh
+			gameStartData, logName := sync.CreateLogSyncgroup(u)
+			settingsName := sync.CreateSettingsSyncgroup(u)
 			if logName != "" && settingsName != "" {
 				sync.LogSettingsName(settingsName, u)
 				u.ScanChan <- true
@@ -156,13 +148,10 @@
 		} else {
 			for _, b := range u.Buttons {
 				if button == b {
-					joinLogDone := make(chan bool)
 					logAddr := b.GetInfo()
-					go sync.JoinLogSyncgroup(joinLogDone, logAddr, u)
-					if success := <-joinLogDone; success {
-						settingsCh := make(chan string)
-						go sync.CreateSettingsSyncgroup(settingsCh, u)
-						sgName := <-settingsCh
+					success := sync.JoinLogSyncgroup(logAddr, u)
+					if success {
+						sgName := sync.CreateSettingsSyncgroup(u)
 						if sgName != "" {
 							sync.LogSettingsName(sgName, u)
 						}
@@ -187,7 +176,7 @@
 			if u.CurTable.AllReadyForNewRound() {
 				pressButton(b, u)
 			}
-		} else if u.CurPlayerIndex < 0 {
+		} else if u.CurPlayerIndex < 0 || u.Debug {
 			for _, button := range u.Buttons {
 				if b == button {
 					pressButton(b, u)
@@ -232,17 +221,15 @@
 			}
 		} else {
 			for key, button := range u.Buttons {
-				if b == button && u.CurPlayerIndex < 0 {
+				if b == button && (u.CurPlayerIndex < 0 || u.Debug) {
 					if key == "joinTable" {
 						u.CurPlayerIndex = 4
 						sync.LogPlayerNum(u)
 					} else {
 						playerNum := strings.Split(key, "-")[1]
-						if u.CurPlayerIndex < 0 {
-							u.CurPlayerIndex, _ = strconv.Atoi(playerNum)
-							sync.LogReady(u)
-							sync.LogPlayerNum(u)
-						}
+						u.CurPlayerIndex, _ = strconv.Atoi(playerNum)
+						sync.LogReady(u)
+						sync.LogPlayerNum(u)
 					}
 				}
 			}
@@ -268,51 +255,21 @@
 			view.LoadTableView(u)
 		} else if b == u.Buttons["hand"] {
 			view.LoadPassOrTakeOrPlay(u)
-		} else if b == u.Buttons["dragPass"] {
-			if b.GetDisplayingImage() {
-				u.CurImg = b
-				for _, img := range u.Other {
-					u.Eng.SetSubTex(img.GetNode(), img.GetAlt())
-					img.SetDisplayingImage(false)
-				}
-				blueBanner := u.Other[0]
-				reposition.BringNodeToFront(u.BackgroundImgs[1].GetNode(), u)
-				reposition.BringNodeToFront(b.GetNode(), u)
-				for _, d := range u.DropTargets {
-					reposition.BringNodeToFront(d.GetCardHere().GetNode(), u)
-				}
-				if blueBanner.GetNode().Arranger == nil {
-					finalX := blueBanner.GetInitial().X
-					finalY := b.GetInitial().Y + b.GetDimensions().Y - blueBanner.GetDimensions().Y
-					finalPos := coords.MakeVec(finalX, finalY)
-					reposition.AnimateImageNoChannel(blueBanner, finalPos, blueBanner.GetDimensions(), u)
-				}
-			}
+		} else if b == u.Buttons["pass"] {
+			pressButton(b, u)
 		}
 	}
 }
 
 func moveClickPass(t touch.Event, u *uistate.UIState) {
-	if u.CurImg != nil {
-		imgs := make([]*staticimg.StaticImg, 0)
-		cards := make([]*card.Card, 0)
-		pullTab := u.Buttons["dragPass"]
-		blueBanner := u.BackgroundImgs[1]
-		imgs = append(imgs, pullTab)
-		imgs = append(imgs, blueBanner)
-		for _, d := range u.DropTargets {
-			imgs = append(imgs, d)
-			cards = append(cards, d.GetCardHere())
-		}
-		for i := 2; i < 7; i++ {
-			text := u.BackgroundImgs[i]
-			u.Eng.SetSubTex(text.GetNode(), text.GetAlt())
-			text.SetDisplayingImage(false)
-		}
-		reposition.DragImgs(t, cards, imgs, u)
-	} else if u.CurCard != nil {
+	if u.CurCard != nil {
 		reposition.DragCard(t, u)
 	}
+	curPressed := findClickedButton(t, u)
+	alreadyPressed := getPressed(u)
+	if len(alreadyPressed) > 0 && len(curPressed) == 0 {
+		unpressButtons(u)
+	}
 }
 
 func endClickPass(t touch.Event, u *uistate.UIState) {
@@ -331,32 +288,50 @@
 				readyToPass = false
 			}
 		}
-		pullTab := u.Buttons["dragPass"]
+		passButton := u.Buttons["pass"]
 		if readyToPass {
-			u.Eng.SetSubTex(pullTab.GetNode(), pullTab.GetImage())
-			pullTab.SetDisplayingImage(true)
-		} else {
-			u.Eng.SetSubTex(pullTab.GetNode(), pullTab.GetAlt())
-			pullTab.SetDisplayingImage(false)
-		}
-	} else if u.CurImg != nil && touchingStaticImg(t, u.Other[0], u) {
-		ch := make(chan bool)
-		success := passCards(ch, u.CurPlayerIndex, u)
-		quit := make(chan bool)
-		u.AnimChans = append(u.AnimChans, quit)
-		go func() {
-			onDone := func() {
-				if !success {
-					fmt.Println("Invalid pass")
-				} else {
-					view.LoadTakeView(u)
+			if passButton.GetDisplayingImage() {
+				u.Eng.SetSubTex(passButton.GetNode(), passButton.GetImage())
+				passButton.SetDisplayingImage(true)
+			}
+			for _, img := range u.Other {
+				if img.GetDisplayingImage() {
+					u.Eng.SetSubTex(img.GetNode(), img.GetAlt())
+					img.SetDisplayingImage(false)
 				}
 			}
-			reposition.SwitchOnChan(ch, quit, onDone, u)
-		}()
+		} else {
+			var emptyTex sprite.SubTex
+			u.Eng.SetSubTex(passButton.GetNode(), emptyTex)
+			passButton.SetDisplayingImage(true)
+			for _, img := range u.Other {
+				if !img.GetDisplayingImage() {
+					u.Eng.SetSubTex(img.GetNode(), img.GetImage())
+					img.SetDisplayingImage(true)
+				}
+			}
+		}
+	}
+	pressed := unpressButtons(u)
+	for _, p := range pressed {
+		if p == u.Buttons["pass"] {
+			ch := make(chan bool)
+			success := passCards(ch, u.CurPlayerIndex, u)
+			quit := make(chan bool)
+			u.AnimChans = append(u.AnimChans, quit)
+			go func() {
+				onDone := func() {
+					if !success {
+						fmt.Println("Invalid pass")
+					} else {
+						view.LoadTakeView(u)
+					}
+				}
+				reposition.SwitchOnChan(ch, quit, onDone, u)
+			}()
+		}
 	}
 	u.CurCard = nil
-	u.CurImg = nil
 }
 
 func beginClickTake(t touch.Event, u *uistate.UIState) {
@@ -554,12 +529,10 @@
 // returns true if pass was successful
 func passCards(ch chan bool, playerId int, u *uistate.UIState) bool {
 	cardsPassed := make([]*card.Card, 0)
-	dropsToReset := make([]*staticimg.StaticImg, 0)
 	for _, d := range u.DropTargets {
 		passCard := d.GetCardHere()
 		if passCard != nil {
 			cardsPassed = append(cardsPassed, passCard)
-			dropsToReset = append(dropsToReset, d)
 		}
 	}
 	// if the pass is not valid, don't pass any cards
@@ -570,19 +543,9 @@
 	for !success {
 		success = sync.LogPass(u, cardsPassed)
 	}
-	// UI component
-	pullTab := u.Buttons["dragPass"]
-	blueBanner := u.BackgroundImgs[1]
-	imgs := []*staticimg.StaticImg{pullTab, blueBanner}
-	for _, d := range dropsToReset {
-		imgs = append(imgs, d)
-		d.SetCardHere(nil)
-	}
-	var blankTex sprite.SubTex
-	for _, i := range imgs {
-		u.Eng.SetSubTex(i.GetNode(), blankTex)
-	}
-	reposition.AnimateHandCardPass(ch, u.Other, cardsPassed, u)
+	imgs := append(u.Other, u.DropTargets...)
+	imgs = append(imgs, u.Buttons["pass"])
+	reposition.AnimateHandCardPass(ch, imgs, u)
 	return true
 }
 
@@ -622,7 +585,6 @@
 	if err := u.CurTable.ValidPlayLogic(c, playerId); err != "" {
 		return err
 	}
-	u.DropTargets[0].SetCardHere(nil)
 	success := sync.LogPlay(u, c)
 	for !success {
 		success = sync.LogPlay(u, c)
@@ -675,8 +637,8 @@
 			}
 			oldY := c.GetInitial().Y
 			suit := c.GetSuit()
-			u.CurCard.Move(d.GetCurrent(), c.GetDimensions(), u.Eng)
-			d.SetCardHere(u.CurCard)
+			c.Move(d.GetCurrent(), c.GetDimensions(), u.Eng)
+			d.SetCardHere(c)
 			// realign suit the card just left
 			reposition.RealignSuit(suit, oldY, u)
 			return true
diff --git a/lib/components/board.dart b/lib/components/board.dart
index 41b072c..bb611ac 100644
--- a/lib/components/board.dart
+++ b/lib/components/board.dart
@@ -76,6 +76,8 @@
 
 class HeartsBoardState extends State<HeartsBoard> {
   Widget build(BuildContext context) {
+    double offscreenDelta = config.isMini ? 5.0 : 2.0;
+
     return new Container(
         height: config.height,
         width: config.width,
@@ -87,24 +89,24 @@
                   ? _buildMiniBoardLayout()
                   : _buildBoardLayout()),
           new Positioned(
-              top: config.height * 5.5,
+              top: config.height * (offscreenDelta + 0.5),
               left: (config.width - config.cardWidth) / 2,
-              child: _buildTrick(
+              child: _buildOffScreenCards(
                   config.isMini ? rotateByGamePlayerNumber(0) : 0)), // bottom
           new Positioned(
               top: (config.height - config.cardHeight) / 2,
-              left: config.width * -4.5,
-              child: _buildTrick(
+              left: config.width * (-offscreenDelta + 0.5),
+              child: _buildOffScreenCards(
                   config.isMini ? rotateByGamePlayerNumber(1) : 1)), // left
           new Positioned(
-              top: config.height * -4.5,
+              top: config.height * (-offscreenDelta + 0.5),
               left: (config.width - config.cardWidth) / 2,
-              child: _buildTrick(
+              child: _buildOffScreenCards(
                   config.isMini ? rotateByGamePlayerNumber(2) : 2)), // top
           new Positioned(
               top: (config.height - config.cardHeight) / 2,
-              left: config.width * 5.5,
-              child: _buildTrick(
+              left: config.width * (offscreenDelta + 0.5),
+              child: _buildOffScreenCards(
                   config.isMini ? rotateByGamePlayerNumber(3) : 3)) // right
         ]));
   }
@@ -161,7 +163,6 @@
         child: new CardCollectionComponent(
             showCard, true, CardCollectionOrientation.show1,
             useKeys: true,
-            animationType: component_card.CardAnimationType.NONE,
             acceptCallback: config.gameAcceptCallback,
             acceptType: isMe && isPlayerTurn ? DropType.card : DropType.none,
             widthCard: config.cardWidth - 6.0,
@@ -319,7 +320,7 @@
             useKeys: true));
   }
 
-  Widget _buildTrick(int playerNumber) {
+  Widget _buildOffScreenCards(int playerNumber) {
     HeartsGame game = config.game;
 
     List<logic_card.Card> cards =
@@ -327,9 +328,18 @@
     // If took trick, exclude the last 4 cards for the trick taking animation.
     if (config.trickTaking && playerNumber == game.lastTrickTaker) {
       cards = new List.from(cards.sublist(0, cards.length - 4));
+    } else {
+      cards = new List.from(cards);
     }
 
-    double sizeFactor = config.isMini ? 1.0 : 2.0;
+    double sizeFactor = 2.0;
+    if (config.isMini) {
+      sizeFactor = 1.0;
+      if (playerNumber != game.playerNumber) {
+        cards.addAll(
+            game.cardCollections[playerNumber + HeartsGame.OFFSET_HAND]);
+      }
+    }
 
     return new CardCollectionComponent(
         cards, true, CardCollectionOrientation.show1,
diff --git a/lib/components/card_collection.dart b/lib/components/card_collection.dart
index 1a2f93f..6ede30d 100644
--- a/lib/components/card_collection.dart
+++ b/lib/components/card_collection.dart
@@ -75,10 +75,12 @@
 class CardCollectionComponentState extends State<CardCollectionComponent> {
   String status = 'bar';
 
-  bool _handleWillAccept(dynamic data) {
-    print('will accept?');
-    print(data);
-    return true;
+  bool _handleWillAccept(component_card.Card data) {
+    return !config.cards.contains(data.card);
+  }
+
+  bool _handleWillAcceptMultiple(CardCollectionComponent data) {
+    return data != config; // don't accept your own self.
   }
 
   void _handleAccept(component_card.Card data) {
@@ -344,7 +346,7 @@
         });
       case DropType.card_collection:
         return new DragTarget<CardCollectionComponent>(
-            onWillAccept: _handleWillAccept,
+            onWillAccept: _handleWillAcceptMultiple,
             onAccept: _handleAcceptMultiple, builder:
                 (BuildContext context, List<CardCollectionComponent> data, _) {
           return new Container(
diff --git a/lib/components/game.dart b/lib/components/game.dart
index c726f92..7a858ad 100644
--- a/lib/components/game.dart
+++ b/lib/components/game.dart
@@ -10,6 +10,7 @@
 import 'package:flutter/scheduler.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/rendering.dart';
+import 'package:vector_math/vector_math_64.dart' as vector_math;
 
 import '../logic/card.dart' as logic_card;
 import '../logic/croupier.dart' show Croupier;
diff --git a/lib/components/hearts/hearts.part.dart b/lib/components/hearts/hearts.part.dart
index 7a1386e..d74946b 100644
--- a/lib/components/hearts/hearts.part.dart
+++ b/lib/components/hearts/hearts.part.dart
@@ -141,15 +141,17 @@
                 .add(HeartsGame.OFFSET_HAND + playerNum);
             break;
           case HeartsPhase.Play:
-            for (int i = 0; i < 4; i++) {
-              if (_showSplitView || i == playerNum) {
+            if (_showSplitView) {
+              for (int i = 0; i < 4; i++) {
+                visibleCardCollectionIndexes.add(HeartsGame.OFFSET_HAND + i);
+                visibleCardCollectionIndexes.add(HeartsGame.OFFSET_TRICK + i);
                 visibleCardCollectionIndexes.add(HeartsGame.OFFSET_PLAY + i);
               }
-              if (_showSplitView) {
-                visibleCardCollectionIndexes
-                    .add(HeartsGame.OFFSET_HAND + playerNum);
-                visibleCardCollectionIndexes.add(HeartsGame.OFFSET_TRICK + i);
-              }
+            } else {
+              visibleCardCollectionIndexes
+                  .add(HeartsGame.OFFSET_PLAY + playerNum);
+              visibleCardCollectionIndexes
+                  .add(HeartsGame.OFFSET_HAND + playerNum);
             }
 
             break;
@@ -252,7 +254,7 @@
         game.debugString = null;
       } catch (e) {
         print("You can't do that! ${e.toString()}");
-        config.game.debugString = e.toString();
+        config.game.debugString = "You must pass 3 cards";
       }
     });
   }
@@ -396,16 +398,44 @@
   String _getStatus() {
     HeartsGame game = config.game;
 
-    // Who's turn is it?
-    String name = _getName(game.whoseTurn) ?? "Player ${game.whoseTurn}";
-    String status =
-        game.whoseTurn == game.playerNumber ? "Your turn" : "${name}'s turn";
+    String status;
+    switch (game.phase) {
+      case HeartsPhase.Play:
+        // Who's turn is it?
+        String name = _getName(game.whoseTurn) ?? "Player ${game.whoseTurn}";
+        status = game.whoseTurn == game.playerNumber
+            ? "Your turn"
+            : "${name}'s turn";
 
-    // Override if someone is taking a trick.
-    if (this.trickTaking) {
-      String trickTaker =
-          _getName(game.lastTrickTaker) ?? "Player ${game.lastTrickTaker}";
-      status = "${trickTaker}'s trick";
+        // Override if someone is taking a trick.
+        if (this.trickTaking) {
+          String trickTaker =
+              _getName(game.lastTrickTaker) ?? "Player ${game.lastTrickTaker}";
+          status = game.lastTrickTaker == game.playerNumber
+              ? "Your trick"
+              : "${trickTaker}'s trick";
+        }
+        break;
+      case HeartsPhase.Pass:
+        if (game.hasPassed(game.playerNumber)) {
+          status = "Waiting for cards...";
+        } else {
+          String name =
+              _getName(game.passTarget) ?? "Player ${game.passTarget}";
+          status = "Pass to ${name}";
+        }
+        break;
+      case HeartsPhase.Take:
+        if (game.hasTaken(game.playerNumber)) {
+          status = "Waiting for other players...";
+        } else {
+          String name =
+              _getName(game.takeTarget) ?? "Player ${game.takeTarget}";
+          status = "Take from ${name}";
+        }
+        break;
+      default:
+        break;
     }
 
     // Override if there is a debug string.
@@ -417,18 +447,53 @@
   }
 
   Widget _buildStatusBar() {
+    HeartsGame game = config.game;
+
+    List<Widget> statusWidgets = new List<Widget>();
+    statusWidgets.add(new Text(_getStatus(), style: style.Text.largeStyle));
+
+    switch (game.phase) {
+      case HeartsPhase.Play:
+        statusWidgets
+            .add(new IconButton(icon: "action/swap_vert", onPressed: () {
+          setState(() {
+            _showSplitView = !_showSplitView;
+          });
+        }));
+        break;
+      case HeartsPhase.Pass:
+      case HeartsPhase.Take:
+        // TODO(alexfandrianto): Icons for arrow_upward and arrow_downward were
+        // just added to the material icon list. However, they are not available
+        // through Flutter yet.
+        double rotationAngle = 0.0; // right
+        switch (game.roundNumber % 4) {
+          case 1:
+            rotationAngle = math.PI; // left
+            break;
+          case 2:
+            rotationAngle = -math.PI / 2; // up
+            break;
+        }
+        if (game.phase == HeartsPhase.Take) {
+          rotationAngle = rotationAngle + math.PI; // opposite
+        }
+        statusWidgets.add(new Transform(
+            transform:
+                new vector_math.Matrix4.identity().rotateZ(rotationAngle),
+            alignment: new FractionalOffset(0.5, 0.5),
+            child: new Icon(icon: "navigation/arrow_forward")));
+        break;
+      default:
+        break;
+    }
+
     return new Container(
         padding: new EdgeDims.all(10.0),
         decoration:
             new BoxDecoration(backgroundColor: style.theme.primaryColor),
-        child: new Row([
-          new Text(_getStatus(), style: style.Text.largeStyle),
-          new IconButton(icon: "action/swap_vert", onPressed: () {
-            setState(() {
-              _showSplitView = !_showSplitView;
-            });
-          })
-        ], justifyContent: FlexJustifyContent.spaceBetween));
+        child: new Row(statusWidgets,
+            justifyContent: FlexJustifyContent.spaceBetween));
   }
 
   Widget _buildFullMiniBoard() {
@@ -467,7 +532,6 @@
                   true,
                   CardCollectionOrientation.show1,
                   useKeys: true,
-                  animationType: component_card.CardAnimationType.NONE,
                   acceptCallback: _makeGameMoveCallback,
                   acceptType:
                       p == game.whoseTurn ? DropType.card : DropType.none,
@@ -480,7 +544,7 @@
       cardCollections.add(new Container(
           decoration:
               new BoxDecoration(backgroundColor: style.theme.primaryColor),
-          child: new Column([_buildStatusBar(), playArea])));
+          child: new BlockBody([_buildStatusBar(), playArea])));
     }
 
     List<logic_card.Card> cards = game.cardCollections[p];
@@ -489,10 +553,8 @@
         dragChildren: true, // Can drag, but may not have anywhere to drop
         comparator: _compareCards,
         width: config.width,
-        useKeys: _showSplitView);
-    cardCollections.add(c); // flex
-
-    cardCollections.add(_makeDebugButtons());
+        useKeys: true);
+    cardCollections.add(new BlockBody([c, _makeDebugButtons()]));
 
     return new Column(cardCollections,
         justifyContent: FlexJustifyContent.spaceBetween);
@@ -602,32 +664,39 @@
 
     Color bgColor = completed ? Colors.teal[600] : Colors.teal[500];
 
+    Widget statusBar = _buildStatusBar();
+
     Widget topArea = new Container(
         decoration: new BoxDecoration(backgroundColor: bgColor),
         padding: new EdgeDims.all(10.0),
         width: config.width,
         child: new Flex(topCardWidgets,
             justifyContent: FlexJustifyContent.spaceBetween));
+    Widget combinedTopArea = new BlockBody([statusBar, topArea]);
 
     Widget handArea = new CardCollectionComponent(
         hand, true, CardCollectionOrientation.suit,
         dragChildren: draggable,
         comparator: _compareCards,
         width: config.width,
+        acceptCallback: cb,
+        acceptType: cb != null ? DropType.card : null,
         backgroundColor: Colors.grey[500],
         altColor: Colors.grey[700],
         useKeys: true);
 
-    return new Column(<Widget>[topArea, handArea, _makeDebugButtons()],
+    Widget combinedBottomArea = new BlockBody([handArea, _makeDebugButtons()]);
+
+    return new Column(<Widget>[combinedTopArea, combinedBottomArea],
         justifyContent: FlexJustifyContent.spaceBetween);
   }
 
   Widget _topCardWidget(List<logic_card.Card> cards, AcceptCb cb) {
     Widget ccc = new CardCollectionComponent(
         cards, true, CardCollectionOrientation.show1,
+        dragChildren: cb != null,
         acceptCallback: cb,
         acceptType: cb != null ? DropType.card : null,
-        animationType: component_card.CardAnimationType.NONE,
         backgroundColor: Colors.white,
         altColor: Colors.grey[200],
         useKeys: true);
diff --git a/lib/logic/hearts/hearts_game.part.dart b/lib/logic/hearts/hearts_game.part.dart
index a635060..8a25172 100644
--- a/lib/logic/hearts/hearts_game.part.dart
+++ b/lib/logic/hearts/hearts_game.part.dart
@@ -207,10 +207,14 @@
       cardCollections[PLAYER_C].length == 13 &&
       cardCollections[PLAYER_D].length == 13;
 
+  bool hasPassed(int player) =>
+      cardCollections[player + OFFSET_PASS].length == 3;
   bool get allPassed => cardCollections[PLAYER_A_PASS].length == 3 &&
       cardCollections[PLAYER_B_PASS].length == 3 &&
       cardCollections[PLAYER_C_PASS].length == 3 &&
       cardCollections[PLAYER_D_PASS].length == 3;
+  bool hasTaken(int player) =>
+      cardCollections[getTakeTarget(player) + OFFSET_PASS].length == 0;
   bool get allTaken => cardCollections[PLAYER_A_PASS].length == 0 &&
       cardCollections[PLAYER_B_PASS].length == 0 &&
       cardCollections[PLAYER_C_PASS].length == 0 &&
diff --git a/manifest.yaml b/manifest.yaml
index f9e8c92..6fbe3fb 100644
--- a/manifest.yaml
+++ b/manifest.yaml
@@ -6,6 +6,7 @@
   - name: av/play_arrow
   - name: action/swap_vert
   - name: navigation/arrow_back
+  - name: navigation/arrow_forward
   - name: navigation/menu
 assets:
   - images/default/classic/down/c10.png