Swansea City’s battle to force their way into the play-off places will go down to the last game of the season after Connor Roberts struck the winner against Bristol City.
The wing-back scored his first goal of the season on the stroke of half-time after Conor Gallagher had been denied by the woodwork.
And it proved enough to settle the contest as Famara Diedhiou missed a second-half penalty for the visitors as Steve Cooper’s side claimed three valuable points.
Results elsewhere mean Swansea will head to Reading on Wednesday night sitting seventh in the table, three points behind Cardiff, who occupy sixth and that final play-off berth.
Cooper had made the one enforced change as Ben Cabango came in for the suspended Kyle Naughton in defence.
The Swans were otherwise unchanged from their draw at Nottingham Forest and they started well, Conor Gallagher whipping a dipping strike just past Daniel Bentley’s right-hand post in the sixth minute.
The Robins responded with a bright attack of their own, former Swan Ashley Williams starting a move that ended with Nahki Wells just failing to get proper contact on a deep cross.
It was proving a tight old affair, which was perhaps unsurprising given five of the previous 12 meetings between the sides had ended goalless.
Andre Ayew came close to breaking the deadlock on 23 minutes after Jake Bidwell had made a surging run to deliver down the left.
The two covering Bristol City defenders got in each other’s way, and Bentley had to palm away Ayew’s improvised hooked effort.
Roberts then flashed a ball across the six-yard box shortly after the first-half drinks break.
At the other end Mike van der Hoorn blocked a deflected Andreas Weimann strike, before bravely diving in to deny Benik Afobe’s follow-up.
Ayew volleyed just wide of the target a few moments later after quickly controlling a poor clearance from Bentley.
The Swans were starting to find their rhythm, and Roberts was so close to converting after a lovely turn and cross from Bidwell.
Lovely build-up from keeper Erwin Mulder right the way into the final third again ended with Swansea heads in hands as Jay Fulton’s angled cross just evaded his fellow white shirts.
The hosts were ratcheting up the pressure, and Rhian Brewster volleyed wide after Fulton and Marc Guehi had worked hard to win possession high up the field.
But the Swans got their reward in the third minute of first-half stoppage time.
Gallagher was denied by the inside of the post as his curling strike beat Bentley, but Roberts made no mistake from the follow-up to find the net and send the hosts back to the dressing room with the interval lead.
The visitors pressed early in the second half and enjoyed a good spell of pressure without managing to work Mulder in the Swansea goal.
Wells headed over after some patient build-up around the edge of the area, and the hosts responded by going up the other end for Brewster to force a diving stop from Bentley, while Ayew had his follow-up well blocked.
Bristol City sent on a trio of substitutes with 23 minutes to play in the form of Diedhiou, Kasey Palmer and Niclas Eliasson and the striker’s first action was to win a penalty as Cabango was adjudged to have brought him down.
But the Robins topscorer struck the post from the spot, and the Swans survived with their lead intact.
The Swans made changes of their own – Liam Cullen and Wayne Routledge for Brewster and van der Hoorn – and the youngster started a smart counter that ended with Ayew shooting straight into the grateful hands of Bentley.
Fulton also worked the Bristol keeper after good hold-up play from Ayew as the game entered the final 15 minutes evenly poised.
But the Swans saw the game out to keep their promotion hopes alive.
Swansea City: Erwin Mulder; Ben Cabango, Mike van der Hoorn (Wayne Routledge 72), Marc Guehi; Connor Roberts, Jay Fulton, Matt Grimes (captain), Jake Bidwell; Conor Gallagher (Yan Dhanda 83), Andre Ayew; Rhian Brewster (Liam Cullen 72).
Unused Substitutes: Josh Gould, Tivonge Rushesha, Aldo Kalulu, Jordon Garrick, Nathan Dyer, Bersant Celina.
Bristol City: Daniel Bentley; Zak Vyner, Jack Hunt (Pedro Pereira 46), Ashley Williams (Famara Diedhiou 68), Tomas Kalas, Jay Dasilva (Kasey Palmer 68); Korey Smith (captain) (Niclas Eliasson 68), Jamie Paterson, Andreas Weimann; Nahki Wells (Marley Watkins 87), Benik Afobe.
Unused Substitutes: Joe Wollacott, Adam Nagy, Callum O'Dowda, Tommy Rowe.
Referee: Darren Bond