text:
Author: ashbysvexit
Zarpes, Prisons, And Junkies (Costa Rica – Part Two)
After departing Bahia Drake, we split the sixty nautical miles we needed to travel north to Quepos in two by stopping for a night at Punta Dominical – another spot along the Costa Rica coast popular with surfers. We were learning very quickly that beaches ripe for surfing were often horrible beaches for dinghy landings. No going ashore, but still a nice anchorage to chill out at for a night.
The following day we made for an anchorage near Manuel Antonio, three miles south of Quepos, which we had to visit on order to be issued a new national zarpe.
Though the international clearing in / clearing out procedures were not necessarily that much more complicated or expensive than other places we had visited, Costa Rica introduced us to national zarpes. International zarpes, by comparison, we had dealt with. This official document issued by the country you are departing declares your next port of call. It is often asked for when you first arrive in a new country. If an authority of a country you are entering doesn't ask for your zarpe it's no big deal. But if they ask for one you don't have, all kinds of problems can arise.
National zarpes seemed effectively the same but were required when moving between states or regions within the country.
They didn't necessarily charge for these, but the process to get them could take the better part of a day to sort out or may require taxi rides to various official offices that often were not conveniently in close proximity to one another. A trip ashore was required to get the zarpe before departing, it had to be during limited office hours or business days, and sometimes included time restrictions. A destination had to be put on the zarpe. Copies of our own documentation had to be provided. Only certain places could issue them or be listed on them. Furthermore, the procedures, processes, and locations had to be sorted out each time. Overall, a giant pain in the ass for us with no clear benefit or necessity that we could fathom.
We had declared Quepos as our destination departing Golfito. We were told it was commonly used. To us, this meant a procedure the Quepos authorities would be more familiar with, thereby making it simpler. Especially with language limitations, trying to complete an obscure government procedure at a location that is not already very familiar with that procedure can be a nightmare.
Obviously, the marina just outside Quepos had decided to take full advantage of anyone not staying at the marina who needed to use their dinghy dock. Thirty five bucks just to tie up! Bastards. They took even more advantage of those choosing to stay at the marina charging boats a daily rate of two dollars and fifty cents a foot – for us that would have been over a hundred dollars a night.
Since the only alternative was a beach landing at the anchorage followed by a round trip taxi ride that would cost close to fifty dollars, we chose a bouncy dinghy ride the three miles to the marina and tied up for the day, adding a provisioning run in town and filling our jerry cans with diesel to try to help offset the marina charge for tying up our dinghy to their dock.
The town of Quepos was pleasant enough to visit, and the process of receiving a new national zarpe there turned out more inconvenient than painful.
A unique approach to crushing cans for recycling
However, at the end of the day, the three mile dinghy ride back to the anchorage was choppy, uncomfortable, and wet. The wind had kicked up, causing the waves to respond in kind. Loaded down with jerry cans now full of diesel and bags of provisions, we had no prayer of getting the dinghy up on a plane. Not pleasant at all. Downright fucking sloppy even.
The anchorage itself, though nice enough, was subject to a lot of panga traffic with an endless supply of tourists during the day, as well as seeming to unfortunately be a popular spot for mega-twats (mega yachts) to anchor at. However, the swell was what eventually drove us onward. One night, we endured the most ridiculous swell movement we had ever experienced, and a lack of any wind at all made using a swell bridle ineffective. It appears we may have to sort out a flopper stopper to cope with this in the not too distant future as we're apparently still quite gun-shy about using a stern anchor after our first fiasco.
All in all, it had taken us five days to sort out everything we needed to before we could move on. Once we were ready, we were gone first thing the following morning.
A number of anchorages later we found ourselves at Isla San Lucas, Costa Rica's version of Alcatraz. From the late 1800's until 1991, this less than two square mile island housed a high security prison globally notorious for the torture and inhumane treatment its prisoners endured; or more often, failed to endure. Their crimes ranged from violent multiple murders to political dissidence.
The water surrounding the prison, like Alcatraz, added an extra security ring. However, in addition to strong currents, there was the added Costa Rican flair of hammerhead sharks and crocodiles. Didn't see hammerheads…did see a croc. Didn't go swimming…did go ashore.
Only a couple of years ago, it was upgraded from Wildlife Refuge to National Park status. Currently restoration efforts include a church, medical facility, and holding cells which can be walked through.
A nominal fee helps to support the ongoing restoration efforts for the prison facilities as well as numerous archeology excavations of thousand year old Indigenous sites. It also grants you access to wandering around the island for a day. Since we hadn't arranged a guided tour, we just had to wing it.
The walls of the cells are covered with graffiti. The musings of a hundred years of misery. Sketched, painted, and carved onto and into the walls, they run the gamut of what you would expect to be on the minds of tortured prisoners— salvation, damnation, freedom, and sex.
The cement disc apparently had something to do with a water storage tank and/or a particularly nasty solitary confinement area for trouble makers.
Random wandering photos…
More than a dozen trails offer the potential for hours and hours of hikes around the island – a welcome counterbalance to the darkly morbid specter of the prison. However, the reality is that skyrocketing afternoon temperatures from an absolutely blistering sun typically quash the plans of all but the hardiest on walkabout.
With only a couple of cruising boats and a couple of day tour pangas in the bay for only a short time during our entire four day stay, we essentially had the entire calm and protected anchorage to ourselves. The visit to Isla San Lucas was well worth the stop.
To be honest, it's the only way one wants to visit a prison… as a tourist.
Now, fresh outta prison – it's a story often heard. It was time to drift back into orbit with our old Junkie friends.
Junkie Reunion
Long before our arrival in Costa Rica, we knew one of our top priorities would be a Junkie reunion. Cindy and Juan, two of our Divemaster students over ten years ago from our days working at Scuba Junkie in Borneo, now lived in Santa Teresa – a small surfing and tourist destination along the coast. We would be sailing right by.
While many of our previous students had gone on to work in the scuba diving industry, Cindy and Juan were the only ones we were aware of who had gone on to own their own dive shop. This was a Junkie reunion that couldn't be missed.
The beach off of Santa Teresa is a really popular surfing spot. Big waves = no dinghy option. And the whole coast was completely unprotected as an anchorage which meant there was no way we could safely leave our boat unoccupied at anchor. We found a small fishing village, Tambor, in a very protected bay called Bahia Ballena on the opposite side of the peninsula, within about a one hour drive from Santa Teresa.
Another sailboat was anchored there as well; and we befriended them enough to feel comfortable leaving Exit at anchor for a few days with eyes upon her and a WhatsApp contact if trouble arose. After a dinghy ride to the cement dock used by the local fishermen (far too scary for us to tie up to) and a brief conversation with one of the fishermen on the dock, we were able arrange a ride with one of the fishermen between our boat and the dock.
Tambor dock
The logistics weren't easy but, in the end, we were ecstatic to have figured it out. Cindy and Juan were fabulous hosts. It was as though ten days had passed since we saw them rather than ten years.
In addition, we had the opportunity to meet their two incredible kids, Marina and Bruno, for the first time, as well as two other diving friends of theirs, Tim and Barbara.
We were astonished to learn they had managed to arrange for us all to stay at a property which provided a base of operations unlike anywhere we could have imagined. Fortunately, the thousand dollar per night rate was waved! The place was decadent; the view was wondrous.
Sometimes it pays to know someone who knows someone…
The nearby beach, at the bottom of the treacherous hill our house was perched upon, provided an afternoon's entertainment of socializing and even attempted surfing.
And, of course, when your ex-students own a dive shop, how can you not go diving?
The day of diving with old friends was incredible. Conditions were pretty challenging with significant swell and shocking visibility (damn red tide). Still, it was the phenomenal people we got to spend time blowing bubbles with that made all the difference.
Iguana Divers. Well done Cindy and Juan. Kudos to you as fabulous divers, businesspeople, parents, and friends!
They told me long ago, on the road…once a Junkie, always a Junkie.
Once a Junkie, always a Junkie
The town of Santa Teresa itself was quite unique. With all the look of a tiny surf town set into the lush Costa Rican landscape, it was hard to juxtaposition the tiny shops and businesses lining a single, mostly potholed dirt road with the completely unexpected caliber of international cuisine available and volume of multi-million dollar houses perched on the hillside. Even more surprising was learning that no less than five billionaires, as well as mega-stars including the likes of Mel Gibson and Tom Brady, have homes in Santa Teresa.
Deja Vú / Full Circle…
Flash back almost exactly two months.
'GO COUGS!'
Shouted by a passing tourist who had spotted our hailing port of Pullman, WA on our transom, they had strangely enough been the first words we had heard approaching Costa Rica, less than five miles from Golfito, the port of call where we would clear into the country.
Now, fifteen anchorages later and four hundred nautical miles further up the coast, less than five miles from the town of Playa del Coco where we would clear out of Costa Rica, we were looking through our binoculars at a crimson flag clearly flying the WSU Cougar logo on the beach of Playa Hermosa.
Deja vú.
Shortly after Kris posted the photo she received a reply from the original flag owner. Apparently a WSU alum visiting Costa Rica on holiday had previously gifted the flag to a fascinated local, with the instructions to return any greeting with 'GO COUGS!'
Small world.
It seemed to be a perfectly fitting full circle moment.
Costa Rica Challenges
In spite of all of the stunning landscapes and remarkable experiences, our time in Costa Rica will be remembered with mixed feelings.
From the perspective of a couple of sea gypsies, Costa Rica was well more challenging than we had ever perceived it would be.
Although the sixteen foot tides which had stunned us initially as we emerged from the Panama Canal in January seemed nearly five feet less in Costa Rica, at times it seemed impossible to escape the Pacific Ocean's relentless southwestern swell that seemed to wrap around every land mass we tried to take refuge behind.
Anchorages seemed farther and fewer in between. Choices in those that existed were more limited. Smaller bays. Moorings to contend with. Steeper banks which forced you either precariously close to shore or deeper than forty feet to anchor. It's hard to set your scope when you swing between twenty five and seventy feet in depth.
And the strategy of just throwing out more chain only works when you know you have a clear space to swing. Knowing you have that clear space relies on good water visibility and/or reliable charts, both of which we found to be in short order here in Costa Rica. It is very disconcerting to increase the diameter of the circle you swing in when you know there may be rocks just under the surface of a low tide that you can pass over during high tide.
Weather forecasts, when consistent and accurate, provide critical information needed to make good decisions. Are conditions conducive to sailing or will we be forced to motor? Is an anchorage safe based upon wind direction? Are there storms developing? When should we move? In Costa Rica, that consistency and accuracy didn't exist. Consequently, we struggled constantly with higher stress levels, frustration, and a general sense that we were always simply guessing instead of making informed decisions.
Dinghy landings, more often than not, were excruciatingly difficult. Breaking waves of three feet can easily capsize the dinghy… a catastrophic event that poses both a safety risk and the very real possibility of destroying the outboard engine. Many beaches could only be reasonably landed on or launched from during the most benign cycle of the tide – high or low slack tide. With only two of each in a twenty four hour period, this made for a very small window…and it also meant unless the trip ashore was not more than an hour, either the landing or the launch would fall outside that safe zone. Hmmmm…a messy landing which subsequently forced you to walk around in wet clothes was never a pleasant prospect; however, getting swamped by a breaking wave launching the dinghy after loading three hundred dollars of provisions we had just purchased was even less appealing. Sometimes simply not going ashore seemed the wiser approach. Oftentimes that turned out to be the conclusion.
Compared to other countries in Central America, Costa Rica proved to be one of the most expensive we visited.
The whole national zarpe requirements just added additional complications.
And then there was the damn red tide.
The morning we arrived in Costa Rica, as we passed through the bay approaching Golfito, we first noticed discolored patches in the water. We attributed these to runoff from the rivers which can sometimes collect with tides and currents.
The patches continued off and on for a month before someone enlightened us that what we were looking at was red tide.
Apparently, these out of control algae blooms typically only last two or three weeks. After three months, we learned that this particular tide was stretching all along the Pacific Coast and was the worst people had ever seen..or smelled. And smell it does. Like the combination of stagnant tropical salt water and rotting plant material that it actually is. Yuck!
Hard to describe and harder to bear.
Sometimes it was possible to see the tendrils of algae in rather clear water pass by the hull. Other times the entire body of water surrounding us became as dark as coffee.
The difference between a mild and severe day…
Had we known just how severe a red tide we would encounter, we certainly wouldn't have changed our water maker membrane in January.
We learned very quickly how important it was, if we were making water, to shut down the system at the first signs a bloom was approaching. It would clog the pre-filters with amazing speed. Better yet, we would try to make water further offshore; though sometimes we could still find the red tide blooms had carried miles out on a falling tide or coastal current.
The pre-filters in the photo illustrate how problematic things can be. Both have just been cleaned. The one on the left has about fifty hours of water making time under fair conditions. The one on the right has only one hour of use but looks far worse because of the red tide water that was drawn through it.
On the occasions we got far enough offshore to seemingly at least temporarily break free of the red tide's chokehold, the difference in water quality was stunning.
Nevertheless, despite the tides, and the weather, and the chart shortcomings, and the lack of protected or viable anchorages, and the swell, and the beach surf, and the cost, and the national zarpes, and the stinking red tide…
…regardless of all the challenges, Costa Rica was well worth the visit.
At times, traveling by land instead of by sea would have made things far easier, and we would have encountered less situations where we had to pass on an adventure because of the logistical impossibilities.
Still, it was worth the visit.
But we had now been in Costa Rica for almost exactly two months. We still had over fifteen hundred miles to travel before reaching the relative safety of the Sea of Cortez… and it was almost June — officially two weeks into the start of the Pacific's hurricane season.
Our choices would soon be quite limited. Either we had to get our asses up to the Sea of Cortez immediately or we would be stuck having to sit through the hurricane season in El Salvador, Honduras, or Nicaragua with few options outside of a handful of marinas and questionable anchorages. This was not a good place to plan on hanging out on the hook for the next six months.
If we opted to just keep going until we reached Mexico, Chiapas was our first port of call that we could clear into. Five hundred miles. And that still left another thousand to go after that to the Sea of Cortez.
A fifteen hundred mile mine field of potential hurricanes
As a sobering reminder of both the risk of sitting and the uncertainties of moving, as well as the very real dangers of being at the wrong place at the wrong time, our decision was being made even more difficult by inconsistent and contradicting weather forecasts.
Yet one forecast stood out prominently, shouting loudly above all the others. It was still over a week away but it certainly had our attention.
If the forecast was accurate, this would be the first named storm of the 2022 hurricane season…Agatha. It would also likely follow a trajectory mimicking our current path, passing right next to Chiapas. But a week out is forever and a day when it comes to forecasting weather. And this was the only model that was currently flashing 'danger'.
Others showed a completely different picture for the future.
Erratic weather forecasts and a potential hurricane were only two of many considerations we were having to process. We had already arrived in Playa del Coco, where we could clear out of Costa Rica. However, once we had cleared out, we were committed to go. The clearing out process would more than likely take a full day; and once that undertaking was completed, the Costa Rican authorities would allow us twenty four hours, at the most, to pick up anchor and physically leave Costa Rican waters. In other words, a major process with a very short window that had to be initiated with incomplete information in hand.
The process of getting everything ready on Exit for a substantial offshore passage; the process of making a big decision regarding a safe weather window that hopefully didn't force us to motor the whole way; the process of getting cleared out…
Not for the feint of heart.
As if there wasn't enough to have to keep track of already, there were also the Papagayo winds that had to be considered.
Departing Playa del Coco, there is a stretch of coastline just to the north that marks one of the narrowest points between the Atlantic and Pacific across the Central American isthmus. It is very susceptible to influence by winds that can come rushing down from the highlands after crossing massive Lake Nicaragua and deliver storm force and, at times, even hurricane force winds reaching far out from the coast. These winds, running contrary to the direction of the Pacific swell, can cause a massive buildup of tall waves with very short intervals between – potentially an extremely dangerous situation for boats. Mariners have to carefully monitor not only the normal weather driven winds, but also the forecasted Papagayo winds which can sometime strengthen or at other times even contradict what should be happening.
It is only about fifty nautical miles across the area of concern, but for us that represents a ten hour stretch of exposure. On more than one occasion, we had been advised the Papagayo winds (and even more so the Tuantapec winds just beyond Chiapas, Mexico) are not to be taken lightly unless you fancy having your ass handed to you.
We continuously looked at forecasts and analyzed things. Eventually, we made the call. We go.
The optimism of rainbows and unicorns…
The clearing out process was, as expected, lengthy and complicated, but achievable thanks to Kris' tenacious online research which uncovered a step by step procedure to follow provided by another sailor who had documented their experience, complete with photos of the building fronts to look for…well done.
We had officially cleared out of Costa Rica; and the clock was ticking.
First light.
Looks like gray skies. A camouflage day for us. We blend right in.
As we pick up anchor, the skies look ominous but not as threatening as the most current weather forecast we have just downloaded on our iPad. Still only one of the four weather models predicts a hurricane will develop…but that model still says it will pass right by our destination of Chiapas, Mexico.
We are a week ahead of it. Best get moving.
Our destination of Chiapas one week after we arrive?
Author ashbysvexitPosted on May 21, 2022 July 14, 2022 Leave a comment on Zarpes, Prisons, And Junkies (Costa Rica – Part Two)
Costa Rica – Part One
Golfito, Costa Rica, once one of the local casualties of a changing banana industry, certainly appeared to have finally found its stride. What had apparently become quite a sketchy location to be not so long ago, now appeared to us a very welcoming and friendly place to clear into.
First time we have seen a "scarecrow" atop one of the fishing boats
Every country has its own clear in procedure. Always an adventure to navigate.
Entering the country in a maritime setting automatically means the port captain is involved, requiring paperwork and possible fees. We, like any other traveller, have to get our immigration papers sorted, requiring paperwork and possible fees. Exit requires clearing through customs, requiring paperwork and possible fees.
Every port is a bit, or a lot, different.
At the time we cleared in, Costa Rica required every tourist entering the country by boat to use an agent to complete the process of clearing in and out. Depending on the country, this service may incur a reasonable or unreasonable fee. Furthermore, depending on the competence of the agent and/or the difficulty of the process, this can be money either well spent or money wasted.
In hindsight, as our exchange with our agent was a positive one, we were much less irritated to learn that the requirement for utilizing agents was dissolved the month after we arrived.
Costa Rica also required a quarantine officer to actually come aboard Exit in order to verify we weren't bringing any pork products into the country. This included filling out a declaration of all meat we had aboard followed by an actual inspection of the refrigerator and freezer.
Fortunately for us, being sent to a Costa Rican prison for international pig smuggling will not be on the itinerary.
Waiting for the quarantine officer at Banana Bay Marina in Golfito
The noteworthy generosity and kindness of Gabriella, manager of Banana Bay Marina, was amazing. Yes, she was being paid as an agent to clear us into Costa Rica. Still, we weren't staying at the marina. So allowing us free access to the dock to tie our dinghy, ability to dispose of rubbish and recyclables, unlimited water if we needed, even actually taking us to the port captain and immigration authorities in a taxi she paid for – were all gestures well above and beyond. Her energy and enthusiasm were contagious. Her endless knowledge of the area cleared up any questions we had, and she offered assistance repeatedly.
It made it even easier to return to the marina for happy hours and decadent meals prepared by someone else.
Except…once we were cleared in, it was time for a major water maker maintenance project.
We had been waging an ongoing battle with the water maker since our arrival in Panama. Yet, it had never failed us by ceasing to make fresh water and we fully understood the importance of settling for functioning versus risking having it out of commission for any length of time. Consequently, we were constantly having to fuss with it while respecting the boundary of "don't fuck too much with something that currently works."
While trapped in Bocas del Toro during the Covid lockdowns, we had managed to get spare sensors, hoses, fittings, pumps, and even a membrane shipped to us; so we had parts for the Spectra. Yet, we couldn't do anything that could even possibly jeopardize our ability to make water since it was one of the top critical systems on our boat to keep us off the grid.
The decision had been made long ago to hold off on any major water maker surgery until we were in the Pacific Ocean. Now that time had finally arrived.
Two sensors, a number of high pressure fittings, an intake hose, and the membrane itself all needed replacement. It required disassembly of nearly the entire water maker — a process during which I found myself experiencing numerous moments of doubt. If I fucked it up, we would be in a seriously bad way. But, in the end, everything was back together with no extra pieces still sitting on the table.
Now we just had to get out of the bay we were in and try everything out a few miles offshore where the water would be clearer. We could have gotten away with testing it where we were anchored, but we were adamant that we would be more selective about the water conditions we would consider making water in this time around…a philosophy which always seems to work better in theory than in practice.
A few miles out the water was not pristine, but we needed verification that everything was working at least. If we had to sacrifice a couple of filters, so be it. A few hours of making water seemed to concur the repair was a successful endeavor.
While we were testing the water maker, we were visited by a couple of very social dolphins. Normally dolphins only stick around the boat to ride the bow wake while we are underway and, even then, it rarely takes long before they grow bored of our sluggish pace. For some reason, these two really took an interest in us, and kept coming around to get a closer look. They were so persistent, eventually I slid into the water. They kept a bit of distance, but we were stunned how long the visit lasted.
"Dolphins Outside Golfito, Costa Rica"
It was one of two absolutely amazing experiences with dolphins here in this area.
A few days later while we were anchored in nearby Bahia Dulce, during a night of particularly intense and bright bioluminescence in the water, we had a visit from what may have even been the same two curious dolphins.
Upon hearing the unmistakable sound of their blowholes as they surfaced we came up on deck to a surreal sight. As the two dolphins swam around the boat, they were surrounded by an amazing glow of eerie green light. But even more unbelievable, the amount of bioluminescence was so intense that every detail of the dolphins were lit up also. A green, glowing line traced the entire outline of each dolphin, perfectly and distinctly, as well as every contour and detail of their bodies. It was almost like a sharp, glowing drawing which had been animated. Like the manta ray scene from the movie "Moana".
Impossible to adequately describe. Impossible to capture with video or photos. Even more impossible to forget.
A re-creation attempting to capture the magic of the moment
Hints that the dry season may be coming to an end…
We had not collected rain catch water since Christmas in San Blas. It wasn't that we didn't want to. The fact was we hadn't had any rain fall on our deck since we had crossed through the Panama Canal in mid-January… definitively the dry season. Despite seeing rain in the distance occasionally, it would be three months and a different country before the first drops of rain once again began to flow into our water jugs — March in Costa Rica.
Still, Costa Rica's rainy season was only beginning to kick off in April. Largely, we had impeccable weather.
Yet, the heat of the Costa Rican sun on our aluminum hull made for some frying pan days and reminded us that the rainy season had its advantages as well.
Temperature on the bare deck in the direct sun
Only a couple hours outside Golfito, Bahia Dulce turned out to be a fabulous location to anchor. For much of the time, we were the only boat at anchor in a huge bay.
Going ashore at Bahia Dulce
Kris conducting a moth rescue via SUP
Costa Rican law prohibits any permanent structures from being built less than fifty meters from the high tide line. Consequently, almost every beach appears completely undeveloped, even if there are inhabitants just behind the tree line.
Such was the case with Dolphin Quest — a family run eco-lodge built on seven hundred acres of largely undisturbed land offering accommodations, tours, and jungle adventures. With a deeply seated philosophy of maintaining pristine wilderness, nurturing self-sustaining ecological practices, and rehabilitating environmental damage, they have managed to successfully cultivate a truly "green" business model.
In the few interactions we had with them, we found them to be very authentic. Touring their extensive yet very well thought out and unobtrusive garden proved to be both fascinating and educational. Afterwards we returned to Exit carrying a large bag of incredibly tasty, organically grown fruits and vegetables, as well as a plethora of various herbs and greens that found their way into our meals for a week after our visit.
After a couple of weeks bouncing back and forth between Golfito and Bahia Dulce it was time to get moving again.
Sixty five nautical miles further up the coast brought us to Bahia Drake, which provided a launching point for additional excursions.
Nearby, Isla Caño was only the second dive trip we paid to go on since we had moved aboard Exit nearly five years ago. While it would never be considered among the dives that top our list, it was a great day. Conditions sucked… the boat ride ten minutes off the island was rough, visibility underwater was horrible at times, it was the coldest water had dived in some time, and we felt remarkably guilty for the other eight guests on the boat (who were all snorkeling, some for only the second time above an invisible reef twenty feet below the surface). Still, our dive guide was very good and we managed to see a surprisingly impressive range of creatures — sharks, turtles, a sea horse, rays, eels, schools of jacks, and even harlequin shrimp (which we had last seen only in Indonesia almost ten years ago).
Also in close proximity was Parque Nacional Corcovado — a two hundred fifty square mile swath of untouched jungle established in Costa Rica as a protected reserve inside one of the most biodiverse regions on the planet.
With our remarkably knowledgable and eagle-eyed guide Carlos, we managed to catch glimpses of a dizzying array of wildlife which included: monkeys, crocodiles, scarlet macaws, tiny frogs, lizards (including the basilisk or "Jesus lizard" which actually runs on water), leaf cutter ants, and tapirs; all amongst the stunning backdrop of the dense Costa Rican rain forest.
You have five seconds to spot the croc before you become a spot on the croc…
Croc spotting
Costa Rica — the coast is rich but beach landings are a bitch! Props go out to the Costa Rican captains, whose skills are astounding. We got to witness these skills again and again, which ultimately resulted in both an immense respect for the local captains as well as a constant and intense fear of capsizing our own dinghy in the beach surf.
Our captain for the Parque Nacional Corcovado tour
We never remotely considered ourselves bird watchers before living aboard Exit. Even after five years, I would consider ourselves more sporadically interested and curious observers rather than avid bird watchers.
Pelicans? Always. The coolest of the cool.
And rare or exceptionally unique birds… sure.
But as often as not, I am more likely to cuss out a bird for taking a big messy dump as it passes overhead, or sits on our spreader, or worst of all – tries to sit at the top of our mast, jeopardizing an expensive wind indicator or antennae.
Occasionally, we are visited by a bird that leaves an indelible impression and happy memory rather than an obnoxious turd, as was the case with an extremely curious and inquisitive blue footed booby that visited us while we were at anchor at Bahia Drake. After watching us for quite some time from the relative safety of the dinghy, it grew more courageous and eventually hopped aboard and took a full tour around Exit's deck.
"Boobie Inspection"
One thing we never grew tired of during our stay was Costa Rica's propensity for breathtaking displays of astonishing cloud formations. Variety and depth seemed to always be the theme.
Author ashbysvexitPosted on April 12, 2022 July 14, 2022 Leave a comment on Costa Rica – Part One
Who's Counting?
Seven hundred twenty nine days. We were counting.
It seemed impossible to really wrap our heads around…except for the fact that we had just lived it.
After arriving in Bocas Del Toro, Panama literally the night before the entire country locked down in reaction to a terrifying new phenomena that was sending shock waves around the globe and causing an identical worldwide reaction – Covid – we had no idea how things would unfold.
Days of uncertainty turned into weeks. Weeks became months. After a year we were moving about on Exit but still trapped in Bocas Del Toro. After eighteen months we had explored a large part of Panama's Atlantic coast but it took twenty one months before we finally cleared the final lock of the Panama Canal and found ourselves in the Pacific Ocean.
Still in Panama.
We were counting down the days that would mark our two year anniversary in the country.
Now it looked as though we might actually get out of Panama before that day arrived. Unless, of course, we didn't.
With our new alternator installed, Exit could once again charge her batteries while underway. We were back in business.
Departing Santa Catalina, we made for Bahia Honda which was less than twenty five miles away. For a couple of days the huge bay provided a protected and calm anchorage for us to relax in…mostly. But it had been a compromise.
Isla Coiba had been our first choice. Coiba and its neighboring islands had a reputation as being the premier go-to location on the Pacific side of Panama for scuba diving, whale sharks and whale watching.
However, a number of factors came into play which altered that trajectory. The alternator had been the first. Had it not gone tits up, we probably would have made straight for Coiba after waking up at Bahia Arenas following our overnight passage from Las Perlas. Now, once the alternator was up and running again, we would be backtracking to get to Coiba.
The second factor turned out to be a high likelihood that we would pay out the ass for access to the islands. A ranger station on the north side of Coiba oversaw the national park and charged a fee for any boats anchoring within the park. As divers and conservationists, we appreciated the effort Panama was making to preserve and protect the area. However, we were having trouble swallowing the amount they were collecting on that behalf. Sixty dollars for the boat plus twenty more per person… one hundred dollars PER DAY! It occurred to us that the area was too big to realistically be patrolled by one boat and it was probably possible to sneak in at the southern most island of Jicaron, over thirty miles from the ranger station. Yet we couldn't get past both the ensuing guilt that would accompany that infraction as well as the fallout and cost if we were discovered.
Over the past four years, we had learned diving from Exit without any outside support was not a simple undertaking. Arriving in the Pacific Ocean, we found this became even more complicated due to the extreme tidal changes. Since transiting the Panama Canal, we had suddenly found ourselves having to deal with ten to fifteen foot tidal swings twice a day, creating both depth and current challenges that had to factored.
Conditions at Coiba could be very sketchy for attempting scuba dives. We might not even be able to dive at all.
As for the whales, it was possible we could see some, but it was out of season. As for whale sharks, we had already cashed in a huge dividend at Isla San Jose in Las Perlas and it seemed unlikely to us that we would hit the jackpot again. Odds were against us.
At potentially one hundred dollars a day, the gamble seemed like too much of a long shot. We would pass on Coiba.
As we set out for Bahia Honda, we were a bit dejected we would miss out on Coiba but ultimately felt certain we had made the right choice. Three hours later that certainty was reinforced.
A lack of wind meant we had been forced to motor sail part of the way. The upside of that was, with our alternator fully functional, not only were we able to charge our battery bank but also make water without having to worry about the power draw.
After two hours of making water with the engine running the wind finally picked up. Not only were we able to sail, we decided the batteries were sufficiently charged that we could run the water maker for another hour to try to top off our water tanks. Thirty minutes later, the peace and quiet of our engine-free sail was shattered by a bang. It sounded a bit like a distant gunshot except that it came from inside Exit.
I hustled down the companionway and started looking around. When I removed the floorboard directly below the water maker I was shocked to see water draining into the bilge. And it was coming at quite a fast rate.
Immediately, we shut down the water maker and closed the seacock which fed sea water to it. The flow rapidly slowed to a trickle. A further inspection of the membrane and pump assembly, located in a locker underneath the bed of the aft berth where the water maker was located, revealed that the source of the sound we had heard and ensuing flood of raw water had been triggered by a high pressure fitting which had blown off.
Fortunately, the seacock was shut off before the water level became a problem. Also fortunate was the fact that we had gotten a rather extensive supply of spare parts for the Spectra water maker shipped to us while we were hauled out at Shelter Bay Marina in June. The specialized hose and high pressure fittings were among those parts… hallelujah. It turned out we needed them.
We arrived at Bahia Honda late in the afternoon and decided to tackle the water maker the following day.
After wrestling with the Spectra for most of the following day, it seemed like everything was once again good to go. We spent one more day in Bahia Honda and then continued on to Islas Secas, a small group of islands just outside the park boundaries of Isla Coiba.
Dolphins underway to Islas Secas, Panama
It wasn't Coiba, but it was near enough we hoped to get some of the same bang without the hundred bucks a day.
We found the furthest southwest island of Islas Secas, both unnamed and uninhabited, to be a stunning anchorage. Incredible shades of blue both above and below the water, an easily accessible and beautiful beach, great holding for the anchor and fair protection from the southern swell.
As had been the case in Las Perlas, there were almost no other sailboats in the area. Both fishing boats as well as boats bringing day trippers made for a lot more traffic at Islas Secas. However, generally we had the anchorage to ourselves by late afternoon.
We also got in our first dive since crossing the Panama Canal. We joked that we would have been unhappy paying a hundred and fifty dollars for a dive company to have taken us, but it was good to be blowing bubbles again. The coral was quite impressive in places, and the abundance of fish and marine critters surpassed anything we had seen for a long, long time.
Even a mediocre dive is better than a great day's work.
Diving at Islas Secas
The most frustrating thing we had encountered recently seemed to be our inability to access any kind of weather report we could rely on. The forecasts were available. They were just highly variable, even contradictory. And more often than not, most all of them were flat out wrong. Throwing chicken bones and reading tea leaves probably would have provided an improvement in accuracy. We were lucky that, with the current dry season, weather was almost entirely benign. Blue skies and very little wind over ten knots. The seemingly schizophrenic wind shifts turned out to be largely cyclical after a few days of observation – east in the morning, south during the day, clocking to west by sundown and north into the night. Not a problem for most of the week we spent there.
If things kicked up from the north we could always move to Isla Cavada nearby.
After ten days at Islas Secas we decided it was time to get motivated and keep moving. Boca Chica, twenty miles away on the mainland was our destination, not for its scenic beauty but, rather, a final provisioning excursion prior to clearing out of Panama.
While we were able to get fuel at a marina in Boca Chica, it became necessary to sort out transportation inland to David, Panama's second largest city, for additional supplies. Fortunately, we were able to arrange a reliable driver that our friend Sharon, from Isla Joya back in Bocas Del Toro, used regularly. His English was impeccable and he was a lifelong resident of David, making it easy to locate the best places to find particular things.
Not only were we able to bring back an entire car load of food, supplies, and alcohol; he also helped us track down more elusive things. Club soda and tonic water were among those, as was a precious impact wrench which had elevated to the top position on my most needed tools list following my recent battles with both our furler and alternator. Ironically, finally being in possession of an impact wrench all but assured that we would never have use for one again.
We even managed to obtain Covid booster shots at a pop-up clinic in a public park. First try and it took no more than fifteen minutes. Quite a different story from our previous shot which had taken more like ten tries over the course of days in multiple cities.
After stowing away all of the goodies we had procured in David and sorting the final logistical details of our impending departure, we even treated ourselves to our first fancy sit down dinner of 2022.
Our final task in Panama was the process of clearing out, for which we needed to go to Armuelles, the last port of call before Costa Rica. Six hours of motoring. At least we could make water along the way.
Over the course of our stay in Panama, our water maker acquired the name Larry. During water making sessions, Kris would ask how the Spectra was doing, and I would answer 'seems happy as Larry.' The name stuck.
It had been three weeks since the high pressure line had blown out. Happy as Larry for the past twenty five operating hours of water making. I had become complacent. I should have known better.
Kris asked how Larry was. Flippantly, I replied, happy as… I would guess. The floorboards aren't floating.
Kris didn't smile. Immediately, I realized the error of my ways. It was too late. The words had already tumbled out of my mouth. I didn't even need to look. As I went below, I could hear an alarm on the Spectra, buzzing at a volume slightly less than audible from the cockpit with the engine running.
It had shut itself down. The bilge was filling with water. The high pressure fitting had blown out again. Fuck. Served me right. It hadn't been fixed correctly. Larry had done left the house, and Jessie had just arrived [an inside joke best left inside].
This was going to require some serious attention, but our water tanks were almost full. It could wait.
Armuelles was the place to clear out of Panama; not to sort out more repair issues.
Despite having heard some nightmare stories about attempted dinghy beach landings in sketchy conditions at Armuelles, we experienced no drama. The clearing out process was easy enough. A man by the name of Omar from the port captain's office met us on the beach and took us back to his office, completed a bundle of paperwork, and then walked us from office to office getting the appropriate stamps and signatures while seeming to berate each office for their inefficiency and slow responses.
With all of our paperwork in order, we were officially cleared out of Panama. Barring a beach ambush by a rabid pack of wild dogs upon returning to our dinghy, it seemed nothing could stop us from setting sail for Costa Rica the following day…
No. No rabid pack of dogs. Only a lone shade seeker.
We had a celebratory mix of Perfect Storm drinks that evening in the cockpit. The toast was to Panama. Panama had provided comfort, safety, and exploration. We couldn't have asked for more. Now it was finally time to go.
Almost two years…but not quite. Seven hundred twenty nine days in Panama. Well over ten thousand hours on the water. Three hundred sixty five of those hours with the anchor up. Approximately nineteen hundred nautical miles traveled aboard Exit since our arrival in Panama.
And yet possibly the most ironic and surprising number? After all that, despite the fact we were now in a different ocean — as the crow flies, we ended up clearing out of Panama only seventy five miles away from the location where we had cleared in.
But who's counting?
We raised anchor and departed Armuelles at sunrise the next morning.
Sunrise at Armuellas
For the second day in a row, our wind indicator provided digitally displayed confirmation of the depressing reality that we would be burning diesel to get anywhere.
The rising sun slowly burned away the morning mist, revealing a shadowy outline of Volcan Baru in the distance behind us. The active volcano's ten thousand foot peak represents Panama's highest point.
Volcan Baru
As we approached the southern tip of Burica Peninsula, looking through binoculars the beach we were seeing was Panama. However, just behind the line of trees was Costa Rica!
Nine hours and fifty or so nautical miles later we were almost there.
Ten or so miles outside Golfito, the first port of call in Costa Rica, the alarm for our autopilot sounded. The same autopilot we had brought back from the States and installed less than six months ago. Fuck. Another battle to be waged when we had mistakenly assumed the war was over. It would have to get in line.
You'd think the elation of arriving at a new country after all this time would be at the front and center of attention.
To the contrary, I was pretty bummed. In twenty four hours we had killed the water maker, nearly fifteen gallons of diesel, and now our auto pilot.
Welcome to Costa Rica.
Approaching Golfito, Costa Rica
Still, the moment turned when a panga full of tourists overtook us in the entrance channel. As the boat carrying a dozen or so pasty white passengers passed by us, a random guy wearing a straw hat held up his fist and yelled something at us that I couldn't quite make out.
Kris said he obviously had seen the "Pullman, WA" on our transom.
Occasionally it prompts the question, "Are you guys from Western Australia?"
Other times, like this, it prompts the response, "Go Cougs!"
I had to smile. We hadn't cleared into Costa Rica yet, but I was already starting to feel at home.
Author ashbysvexitPosted on March 17, 2022 July 14, 2022 Leave a comment on Who's Counting?
The Right Tool
To-day… Tue-sday… month zero-tw0, day two-two, year two-zero-two-two. Right day, wrong to-ol. Might as well be two-twenty-two in the afternoon.
Okay. That's it. Enough of the tooooos.
Second repair in a row. An impact wrench is now officially on the list.
The impact wrench wouldn't have sorted everything with the furler repair that we completed just before our Panama Canal transit, but it sure as hell would have helped.
This time it made all the difference.
We departed from Las Perlas cautiously optimistic. Erratic weather forecasts left us completely guessing on what we would encounter once underway. Leaving early in the afternoon provided us with an option to bail out the following morning just after clearing Punta Mala (Bad Point…great name) if things were going against us, or continue if things were looking good.
After threading our way through an endless convoy of ships approaching and departing the Panama Canal, we had nothing but open ocean to let Exit run. Only the occasional fishing boat crossed our path, surprisingly small for how far away we were from anywhere.
The Predict Wind calculation of ten hours to get around Punta Mala seemed more than optimistic. It was the best of four forecast calculations ranging from ten to seventeen hours. Amazingly, it actually took us only nine hours [yes…for dirt dwellers doing the calculation, that averaged a snail's pace of slightly more than twice walking speed, or not even that if you are a fast walker].
During the day we were treated to a visit by a pod of enthusiastic dolphins, quite different from those we had seen in the Atlantic.
For us, it was an epic, exhilarating sail from Las Perlas to Punta Mala, during which we averaged a screaming pace of over eight knots for the entire nine hours. Even once the darkness of night had set in, an incredible full moon illuminated the watery path before us. Eventually, after passing Punta Mala, we found ourselves having to finally turn on our engine when the wind completely died.
Despite our own reluctance to run the engine, our battery bank would be much more appreciative of the gesture.
Yet to our dismay, even with the Perkins diesel engine churning away, the number on the ammeter remained negative and the charge deficit continued to grow.
The alternator wasn't charging at all.
By sunrise, the charge deficit showing on the digital display was the highest it had been since we had replaced the entire house battery bank at the end of our haul out in June. At least now, as the sun climbed higher and higher, our solar panels were being fed and the batteries would begin to recover.
Twenty one hours and one hundred fifty nautical miles after departing Isla San Jose in Las Perlas, we set the hook in the expansive though calm bay of Bahia Arenas and immediately chose sleep over troubleshooting.
By sundown a number of tests had confirmed what we already suspected. The alternator was dead. No output at all.
It was, by no means, an immediate emergency.
The house batteries were only six months old and were getting nearly fully charged every day just with solar collection. Currently, Panama was in its dry season. We hadn't collected a drop of rain catch since before Christmas.
Still, our location pretty much qualified as middle of no where and we really needed our alternator running.
Unfortunately, diagnosing the exact problem inside the alternator was a bit academic since we had no spare parts aboard that would facilitate a repair.
Fortunately, during our recent visit to the States, Kris managed to convince me that the overall benefits of having a spare alternator aboard Exit far outweighed the overall pain in the ass of carrying that alternator back to Exit.
No parts… but we actually had the entire brand new alternator. Sweet. The only problem was we only had one pulley. On the broken alternator. And it was not going to budge.
Hours became days.
I tried everything except the one thing I knew would work but didn't have… an impact wench.
Half inch drive socket wrench.
Half inch drive socket wrench with a breaker bar.
Half inch drive socket wrench with an even bigger breaker bar.
The leverage didn't matter because the damn pulley kept spinning.
Holding it with a towel didn't help.
Wrapping a belt around the pulley slipped.
An oil filter wrench didn't work.
Channel locks couldn't stop the pulley.
The nut was too tight; the pulley wouldn't stop turning; the alternator itself couldn't be secured well enough.
It became an exercise in frustration.
Eventually, desperate and somewhat delirious, I concocted what I deemed would be a victorious apparatus of MacGyver proportions to get that damned pulley off — a V-belt with the teeth wrapped around the alternator fan to grip, twisted into a tourniquet and held by the handle of a hammer, supplemented with a pair of channel locks holding the pulley which had been wrapped with electrical tape for protection, all wedged against a deck cleat and held in place by one person while a second got on the 22mm nut with a socket attached to a two foot breaker bar.
Confident that this would be our moment of glory, I held tight and Kris leaned into the breaker bar.
It slipped immediately.
Arrrrrrrrrrrrrrrrrrrrrgh.
Cursing my lack of an impact wrench for the thousandth time, I crawled back into the engine compartment and completely reattached the damn alternator and tightened the belt.
Why? To remove it, of course.
The final option.
The thinking was: the alternator would be bolted to the engine block to keep it secure from movement; the belt would be tensioned back up as tight as possible to keep it gripping the pulley; the engine compression would hold it all in place against the torque. Why hadn't we tried it earlier? Because it was a giant pain to reattach something that had to come right back off again.
Plus, it didn't do shit. After all that, the pulley rotated backwards with almost no effort applied.
Like Charlie Brown and that damn football.
The alternator got pulled out once again.
Fuck.
We concluded that Santa Catalina, a bit less than forty miles away, was the only possibility for the near future. If we hoped to either get the broken alternator repaired or, at the least, have the pulley removed so the new alternator could be used, we'd need to get to a town of some sort.
Consequently, we enjoyed five hours of brilliant sailing followed by two solid hours of smashing headlong into three to five foot waves directly on our bow in winds surpassing thirty knots at times. Oh, the constantly swinging pendulum.
Varying conditions come with the territory, sure. But thirty degree wind shifts accompanied by nearly instantaneous fifteen knot increases in wind speed make for some pretty interesting moments.
With the anchor finally set outside a tiny town known more by surfers than typical tourists, we waited another thirty six hours for the wind to die down enough for us to brave a beach landing in our dinghy.
Sailboat repair, even in a local fishing village, would be a long shot. But car repairs would not. And our alternator was no different from a car alternator. As luck would have it, Santa Catalina appeared to have more cars than anyplace we had been since Panama City.
Kris had learned of a possible source of help from a couple on another boat at anchor who had already been ashore. A guy by the name of Senior Roberto who owned the first restaurant on the right.
When we got there, the restaurant was closed. A guy sitting at one of the outside tables informed us Senior Roberto was gone until afternoon. He tried to call but no one picked up.
As best we could, we tried to communicate our situation and need for a mechanic, machine shop, or car repair shop. We pulled the alternator out of the backpack to help clarify.
While the guy was looking through the contact list on his phone, trying to figure who he knew that could possibly help us, another man walked past the restaurant in the middle of the road. He looked over, saw the alternator, and asked, "Mechanico?"
"Si," we nodded.
He smiled and tilted his head slightly, indicating further up the road, then gestured with his hand for us to follow.
A short walk brought us to a driveway that had a half dozen or so guys standing around watching two other guys work on the lower unit of an outboard engine.
They all seemed genuinely friendly and interested. After a bit of circuitous back and forth [saying something is broken is pretty straightforward, however, explaining what you need is not], we were told to come back with the other alternator as well. The guy was confident he could get us sorted out.
Back to the dinghy, which was now sitting on a sprawling beach about fifty yards from the water, thanks to a twelve foot lowering tide. This was one time we especially heralded the joy of having purchased a used $350 set of dinghy wheels for $40 in Bocas Del Toro over a year ago, anticipating this very need once we reached the mighty Pacific Ocean.
Back across a treacherous stretch of water rife with numerous dinghy puncturing rocks lurking just under the water's surface. Back to Exit. Pick up the second alternator. Back in the dinghy. Back across the treacherous stretch of water rife with numerous dinghy puncturing rocks lurking just under the water's surface. Another beach landing. Since the tide would be rising soon and we didn't know how long we'd be, we had to haul the dinghy back to almost the same spot we had left it the first time. Back up the road, past the restaurant, to the driveway with the half dozen or so guys still working on the outboard bottom unit.
It took him about two seconds.
The impact wrench was so fast he was able to hold the pulley with his bare hand.
Without parts, the actual repair of our old alternator would have to wait. But the new one could now be installed.
It all went back together without a hitch. And just like that, once again, we had an alternator that could charge batteries.
VICTOOOOOOOOOOOOOOOORRRRRRRRRRRRRYYYYYYYYYYYY!
An impact wrench is now officially at the top of my priority list for acquisition… before something else breaks.
Like pissing into the wind or taking a dump upside-down, using the wrong tool may ultimately get the job done, but its gonna make things a lot more complicated than necessary, a lot more messy than necessary, and inevitably come with unforeseen consequences.
So let that be a lesson to you kids – always use the right tool for the job… and only take care of business right side up with your back to the wind.
A long look from a very curious pelican
Author ashbysvexitPosted on February 22, 2022 March 13, 2022 Leave a comment on The Right Tool


Sunderland manager Ricky Sbragia has brushed off speculation over his future as he attempts to drag the club out of relegation trouble.
The Scot saw his side fail to secure their Barclays Premier League future on Monday evening when they squandered a 1-0 lead at Portsmouth to lose 3-1 and left themselves among the strugglers at the foot of the table.
Sbragia's men host Chelsea at the Stadium of Light on Sunday knowing defeat could leave them intensely vulnerable, with Hull only a point behind them and derby rivals Newcastle just a further point worse off.
He will prepare for the game against a background of persistent rumours that he will be replaced at the helm whatever happens at the weekend should American businessman Ellis Short push ahead with plans to buy out the Drumaville consortium which took control on Wearside three years ago.
However, despite a posse of higher-profile managers having been linked with his job – Louis van Gaal was mentioned in dispatches before being appointed by Bayern Munich last week, while Steve McClaren and Martin Jol are among those who have also been touted as possible replacements – Sbragia is refusing to be deflected from his mission.
He said: 'I actually don't read it. I'm not interested in that, to be honest with you.
Chairman Niall Quinn, the man who fronted Drumaville's takeover at the Stadium of Light, reaffirmed his commitment to the club he served with such distinction as a player after the defeat at Fratton Park regardless of what happens on Sunday.
However, there is a growing feeling that Sbragia, who took over from Roy Keane when he sensationally resigned in December, could return to his role as first-team coach after a dreadful run of results which has seen the club plunged back into the thick of the battle for survival.


>Congratulations to Steve Manass, an AAM 2014 Annual Meeting Fellowship Recipient!
Congratulations to Steve Manass, an AAM 2014 Annual Meeting Fellowship Recipient!
As an honored Graduate Fellow, Steve Manass attended this year's American Alliance of Museum's (AAM) Annual Meeting in Seattle. Steve is a Graduate Student in Latin American Studies with an emphasis in Anthropology, Pre-Columbian Art History, and Museum Studies and also works for the Museum Studies Program as a Graduate Assistant. Upon his return, Steve noted that AAM provided a platform for him to connect with other museum specialists, witness alternative engagement strategies, experience several types of cultural management practices, and learn the variety of professional development options for further growth.


If there's a special gear-happy someone on your nice list with a wanderlust, these luxury travel gifts will satisfy the gadabout gadget lover.
Few stops along the silk road can boast a history as deep or a culture as remote as Turpan, a region time and the desert refuse to claim.
Nature lovers, thrill seekers, culture vultures, and every other shade of parent will all find family-friendly activities for their ankle-biters in Shanghai.
It's a long time to go to the Singapore Grand Prix, but if you want the best views in the city, these luxury hotels are right on the track.
Whether it's authenticity from Italy or Shanghainese experimentation, diners walking the Bund will find fine food all along China's most famous view.


text:
"""A tool to add {{ _("...") }} around text in template files.

This works for jinja2 template files (and presumably django template
files) and handlebars template files.

This is part of a process to make a non-i18n-aware jinja2 file
i18n-aware.  i18n-ness support is mostly a matter of marking
natural-language text in the file that needs to be translated.  While
some complicated natural language constructs (like plurals) require a
bit more work, the simple case is very simple: replace

Hello world

with

{{ _("Hello world") }}

This script does that, or at least attempts to -- it makes not
pretentions toward perfection.  For situations where it's pretty sure
there's natural language text but it doesn't know how to handle it, it
will insert text like this:

{{ _TODO("Hello {{world}}") }}

This will require manual cleanup before the template file is usable.

Note that this is just a tool, and will not capture all
natural-language text.  For instance, a manual pass will still be
needed to detect natural-language text inside
(1) javascript Weird

Has the following segments
   '', 'Hi, there!', '', '', 'Weird', ''.
(Note the contents of the script-tag are a single segment; the lexer
is smart enough to handle CDATA inside  or </style> tag is 'interesting'.)
    INTERESTING_NORMAL = re.compile('<')
    INTERESTING_CDATA = lambda cls, tag: re.compile(r'</\s*%s' % tag, re.I)

    TAGFIND = re.compile('[a-zA-Z][-.a-zA-Z0-9:_]*(?:\s+|(?=>|/>))')
    ENDTAGFIND = re.compile('</\s*([a-zA-Z][-.a-zA-Z0-9:_]*)\s*>')
    PICLOSE = '>'

    _ATTR_NAME = r'[a-zA-Z_][-.:a-zA-Z0-9_]*'

    # just 'attr', not 'attr=value'.
    BARE_ATTR = re.compile(r'(?P<attr>%s)(?:\s+(?!=)|(?=>|/>))' % _ATTR_NAME,
                           re.DOTALL)

    ATTR_AND_VALUE = re.compile(
        r'(?P<attr>%(attr)s)\s*=\s*'
        r"(?:'(?P<lita>(?:[^\'])*)'"    # LITA
        r'|"(?P<lit>(?:[^\"])*)"'       # LIT
        r'|(?P<bare>(?:[^\'\">\s])+)'   # bare value
        r')(?:\s+|(?=>|/>))'   # end
        % {'attr': _ATTR_NAME}, re.DOTALL)

    def __init__(self, callback):
        markupbase.ParserBase.__init__(self)
        # markupbase makes some callbacks we don't care about.
        for fn in ('handle_comment', 'handle_decl', 'unknown_decl'):
            setattr(self, fn, lambda *args, **kwargs: None)

        self.callback = callback
        # initialize line number and position
        self.reset()

    def _call_callback(self, segment, segment_separates_nltext):
        """Call the callback and update self.callback_outputs."""
        # We just accumulate all the non-None callback-outputs, and
        # let HtmlLexer.parse() figure out which of the 3 cases
        # (listed in HtmlLexer.__doc__) we fall under.
        retval = self.callback(segment, segment_separates_nltext)
        if retval is not None:
            self.callback_outputs.append(retval)

    def error(self, message):
        raise HTMLParser.HTMLParseError(message, self.getpos())

    def set_cdata_mode(self, tag):
        self.interesting = self.INTERESTING_CDATA(tag)

    def clear_cdata_mode(self):
        self.interesting = self.INTERESTING_NORMAL

    def is_cdata_tag(self, tagname, attrs):
        if tagname == 'style':
            return True

        if tagname == 'script':
            # True for 'normal' script tags, but some people sneakily
            # abuse the script tag to store html content:
            # cf. http://ejohn.org/blog/javascript-micro-templating/
            # They tend to use type=text/html, so we look for that.
            for (attr, value) in attrs:
                if attr == 'type' and 'text/html' in value:
                    return False
            return True

        return False

    def handle_data(self, rawdata, start, end):
        """Handles everything between the html tags.  Excludes script data."""
        # By definition, 'data' is natural language text: this is
        # called for everything that's not meaningful html.
        if self.interesting == self.INTERESTING_NORMAL:
            self._call_callback(rawdata[start:end],
                                segment_separates_nltext=False)
        else:
            # Inside a script-tag, there's no such thing as nltext.
            self._call_callback(rawdata[start:end],
                                segment_separates_nltext=True)

    def handle_tag(self, rawdata, start, end, tagname, attrs):
        """Handles a start tag or an end tag.  Excludes comments, PI, etc."""
        segment_separates_nltext = tagname not in INLINE_ELEMENTS

        # As a special case, we treat '

' as a text separator,
        # since people often use it as a substitute for '
'.  We
        # *could* look for
\n
, etc. but there's no need yet.
        if (not segment_separates_nltext and
            rawdata.startswith('

', start) or
            rawdata.startswith('

', start)):
            segment_separates_nltext = True

        # Another special case: certain classes (defined at the top
        # of the file) indicate a tag is separating natural language.
        if not segment_separates_nltext:
            for (attr, val) in attrs:
                if (attr == 'class' and
                    _NATURAL_LANGUAGE_SEPARATOR_CLASSES_RE.search(val)):
                    segment_separates_nltext = True
                    break

        self._call_callback(rawdata[start:end], segment_separates_nltext)

    def parse_to(self, to, i):
        """Find the next occurrence of 'to', past i, and return pos past it."""
        end = self.rawdata.find(to, i)
        if end == -1:
            self.error("No end %s found" % to)
        return end + len(to)

    def parse_starttag(self, rawdata, i):
        """Given the beginning of a start tag, return its end location."""
        tagstart = i

        # First, find the tagname.
        match = self.TAGFIND.match(rawdata, i + 1)
        assert match, 'unexpected call to parse_starttag(): pos %s' % i
        tag = rawdata[i + 1:match.end()].lower().strip()
        i = match.end()

        attrs = []
        value_poses = []        # (start, end), from position of opening <
        while not (rawdata.startswith('>', i) or rawdata.startswith('/>', i)):
            # Check if we have a complete attr-value pair: an attribute
            # name that doesn't end with an =.
            match = self.BARE_ATTR.match(rawdata, i)
            if match:
                attrs.append((match.group('attr'), None))
                value_poses.append((None, None))
                i = match.end()
                continue

            match = self.ATTR_AND_VALUE.match(rawdata, i)
            if match:
                attr = match.group('attr')
                # one of the LITA/LIT/<bare> groups contains the
                # value, the others contain None (it depends on the
                # value format).
                for n in ('lita', 'lit', 'bare'):
                    if match.group(n) is not None:
                        attrs.append((attr, match.group(n)))
                        value_poses.append((match.start(n) - tagstart,
                                            match.end(n) - tagstart))
                        break
                else:     # for/else: no value found  ('<tag attr=>')
                    attrs.append((attr, ''))
                    value_poses.append((0, 0))
                i = match.end()
                continue

            self.error('Malformed "%s" tag' % tag)

        if rawdata.startswith('>', i):
            if self.is_cdata_tag(tag, attrs):
                self.set_cdata_mode(tag)
            return (i + 1, tag, attrs, value_poses)
        elif rawdata.startswith('/>', i):
            return (i + 2, tag, attrs, value_poses)
        raise AssertionError("we should not get here!")

    def parse_endtag(self, rawdata, i):
        """Given the beginning of an end tag, return its end location."""
        match = self.ENDTAGFIND.match(rawdata, i)  # </ + tag + >
        if not match:
            self.error("Incomplete or bad end tag")
        tag = match.group(1).strip()

        self.clear_cdata_mode()
        return (match.end(), tag)

    def parse_entity(self, i):
        """Parse the 'interesting' entity at self.rawdata[i].

        This is called for every place in self.rawdata that matches
        self.interesting.  We should parse the entity that exists
        at this location, call the proper callbacks for it, call
        self.updatepos(i, end_of_entity), and return end_of_entity.

        Arguments:
            i: an offset into self.rawdata.

        Returns:
            An offset into self.rawdata pointing to the end of the
            entity at position i, or None if there was no
            'interesting' entity at position i after all.
        """
        rawdata = self.rawdata
        if rawdata.startswith('<', i):
            if self.TAGFIND.match(rawdata, i + 1):
                (entity_end, tagname, attrs, _) = self.parse_starttag(rawdata,
                                                                      i)
                self.handle_tag(rawdata, i, entity_end, tagname, attrs)
            elif rawdata.startswith("</", i):
                (entity_end, tagname) = self.parse_endtag(rawdata, i)
                self.handle_tag(rawdata, i, entity_end, tagname, [])
            elif rawdata.startswith("c', but...
                self._call_callback(rawdata[i:entity_end],
                                    segment_separates_nltext=True)
            elif rawdata.startswith("<?", i):
                entity_end = self.parse_to(self.PICLOSE, i)
                self._call_callback(rawdata[i:entity_end],
                                    segment_separates_nltext=True)
            elif rawdata.startswith("<!", i):
                entity_end = self.parse_declaration(i)  # in markupbase
                self._call_callback(rawdata[i:entity_end],
                                    segment_separates_nltext=True)
            else:
                entity_end = i + 1
                self.handle_data(rawdata, i, entity_end)
            return self.updatepos(i, entity_end)

        return None

    def parse(self, rawdata):
        """Lex the contents of rawdata, calling a callback for each segment.

        Arguments:
           rawdata: the document to parse.

        Returns:
           Forwards the return value of the callback as described in
           HtmlLexer.__doc__.
        """
        markupbase.ParserBase.reset(self)
        self.rawdata = rawdata
        self.callback_outputs = []
        self.interesting = self.INTERESTING_NORMAL

        i = 0
        n = len(rawdata)
        while i < n:
            # This parser works like a two-stroke engine, repeatedly
            # doing the following two steps:
            # 1) Parse everything up to the next html tag as a single
            #    'data' segment.  This is always a natural-language
            #    text segment.
            # 2) Parse the next html tag as a single 'data' segment.
            #    This may or may not be a natural-language segment,
            #    depending on the tag.
            match = self.interesting.search(rawdata, i)  # <
            if match:
                entity_start = match.start()
            else:
                entity_start = n

            # 1) Everything up to the next tag.
            if i < entity_start:   # handle text up until the next entity
                self.handle_data(rawdata, i, entity_start)
            i = self.updatepos(i, entity_start)

            # 2) The next tag.
            if i < n:
                i = self.parse_entity(i)
                assert i is not None, "interesting.search() lied"

        assert i == n, (i, n)
        if self.callback_outputs:
            # Case (1) from HtmlLexer.__doc__: If the callback has
            # been returning values, then treat them as strings, and
            # concatenate and return them.  We need to make our
            # promised 'cleanup' callback call first, though.
            self._call_callback(None, segment_separates_nltext=True)
            return ''.join(self.callback_outputs)
        else:
            # Case (2) (and (3)) from HtmlLexer.__doc__: if the
            # callback has returned None up until now, we should
            # just forward the value of the 'cleanup' callback call.
            return self.callback(None, segment_separates_nltext=True)

class Jinja2HtmlLexer(HtmlLexer):
    """A version of HtmlLexer that can handle jinja2 markup.

    This is just like the html lexer, except it handles jinja2
    entities in a similar way to html tags.  Like html tags,
    jinja2 entities -- variables, blocks, comments -- either
    divide up runs of natural language, or can be included in a
    run of natural language.  Blocks and comments divide up runs,
    while jinja2 variables are included in runs.

    The only tricky bit is parsing tag names and attributes: jinja2
    elements can be inside html tags, gumming up the works.  So
    BARE_ATTR and ATTR_AND_VALUE are a little complicated.

    We need a specialized parser for this because jinja2 markup is
    processed *first*, before html markup, by the jinja2 template
    engine.  So we need a parser that treats it as a first-class
    element (and doesn't think {#  #} is a tag).
    """
    INTERESTING_NORMAL = re.compile('<|{{|{#|{%')
    # TODO(csilvers): make sure we ignore end </'s in {# jinja2 comments #}
    INTERESTING_CDATA = lambda cls, tag: re.compile(r'</\s*%s' % tag, re.I)

    # Matches {{[not }}]*}}
    _J2_VAR = r'{{(?:}?[^}])*}}'
    # Matches {% foo %}...{% else %}...{% etc %}...{% endfoo %}
    # Also, {%- foo -%}...{%- else -%}...{%- etc -%}...{%- endfoo -%}
    _J2_BALANCED_BLOCK = r'{%-?\s*(?!end).*?{%-?\s*end[^%]*%}'

    # Allow '
'.
    TAGFIND = re.compile('[a-zA-Z][-.a-zA-Z0-9:_]*(?:\s+|(?=>|/>|%s))'
                         % _J2_BALANCED_BLOCK)

    # A 'bare' attribute (without a value) can look like an attribute-
    # name, or it can look like a jinja2 variable or a jinja2 block,
    # but either way it's followed by a space or tag-end, and not by an
    # equals.  (If it's a block, it could be an attribute-value pair;
    # it's too annoying for us to figure it out.)
    # NOTE: an attribute can end with a space, or with the tagend marker
    # (> or />).  In jinja2-land, it can also end with a jinja2 block:
    #
    BARE_ATTR = re.compile(
        r'(?P<attr>%s|%s|%s)(?:\s+(?!=)|(?=>|/>|%s))'
        % (HtmlLexer._ATTR_NAME, _J2_VAR, _J2_BALANCED_BLOCK,
           _J2_BALANCED_BLOCK),
        re.DOTALL)

    # Otherwise, the attribute has a value.  In this case, we require the
    # attribute be named explicitly (no '{{attr}}=1'), but the value can
    # be a raw string, a "quoted string" or 'quoted string', a {{jinja2
    # variable}}, or a {%jinja2 block%}.
    ATTR_AND_VALUE = re.compile(
        r'(?P<attr>%(attr)s)\s*=\s*'
        r"(?:'(?P<lita>(?:%(j2v)s|%(j2b)s|[^\'])*)'"    # LITA
        r'|"(?P<lit>(?:%(j2v)s|%(j2b)s|[^\"])*)"'       # LIT
        r'|(?P<bare>(?:%(j2v)s|%(j2b)s|[^\'\">\s])+)'   # bare value
        r')(?:\s+|(?=>|/>|%(j2b)s))'           # end
        % {'attr': HtmlLexer._ATTR_NAME,
           'j2v': _J2_VAR, 'j2b': _J2_BALANCED_BLOCK},
        re.DOTALL)

    def parse_entity(self, i):
        # First, see if it's an html entity.
        retval = HtmlLexer.parse_entity(self, i)
        if retval is not None:
            return retval

        # OK, maybe it's a jinja2 entity.
        # 3rd value here is true for tags that break up natural language runs.
        tags = (('{#', '#}', True),
                ('{%', '%}', True),
                ('{{', '}}', True),

                # We never try to merge already-marked-up i18n
                # text with its neighbors.
                ('{{ _(', '}}', True),
                ('{{ _TODO(', '}}', True),
                ('{{ i18n_do_not_translate(', '}}', True),
                ('{{ ngettext(', '}}', True),
                # ('{{', '}}', False)
        )

        rawdata = self.rawdata
        for (starttag, endtag, segment_separates_nltext) in tags:
            if rawdata.startswith(starttag, i):
                entity_end = self.parse_to(endtag, i)
                self._call_callback(rawdata[i:entity_end],
                                    segment_separates_nltext)
                return self.updatepos(i, entity_end)

        # This is probably impossible: if we get here, it means
        # self.INTERESTING matched something not present in `tags`.
        return None

class HandlebarsHtmlLexer(HtmlLexer):
    """A version of HtmlLexer that can handle handlebars markup.

    This is just like the html lexer, except it handles handlebars
    entities in a similar way to html tags.  Like html tags,
    handlebars entities -- variables, blocks, comments -- either
    divide up runs of natural language, or can be included in a
    run of natural language.  Blocks and comments divide up runs,
    while handlebars variables are included in runs.

    We need a specialized parser for this because handlebars markup is
    processed *first*, before html markup, by the handlebars template
    engine.  So we need a parser that treats it as a first-class
    element (and doesn't think {{!  }} is a tag).
    """
    INTERESTING_NORMAL = re.compile('<|{{')
    # TODO(csilvers): make sure we ignore end </'s in {{! comments }}
    INTERESTING_CDATA = lambda cls, tag: re.compile(r'</\s*%s' % tag, re.I)

    # Matches {{[not }}]*}}
    _HBARS_VAR = r'{{{?[^!#/>](?:}?[^}])*}?}}'
    # Matches {{#foo}}...{{else}}...{{etc}}...{{/foo}}
    # We need to define this multiple times because the ?P= requires
    # unique names.  We use _HBARS_BALANCED_BLOCK 4 times in ATTR_AND_VALUE,
    # and each of those times needs a different (?P<name>...).
    # Passing in different values for 'i' provides that.
    _HBARS_BALANCED_BLOCK = lambda i: (
        r'{{#\s*(?P<hbb%d>[^\s}]+).*?{{/\s*(?P=hbb%d)\s*}}' % (i, i))

    # Allow '
'.
    TAGFIND = re.compile('[a-zA-Z][-.a-zA-Z0-9:_]*(?:\s+|(?=>|/>|%s))'
                         % _HBARS_BALANCED_BLOCK(1))

    # These are similar to Jinja2HtmlLexer.BARE_ATTR.
    BARE_ATTR = re.compile(
        r'(?P<attr>%s|%s|%s)(?:\s+(?!=)|(?=>|/>|%s))'
        % (HtmlLexer._ATTR_NAME, _HBARS_VAR, _HBARS_BALANCED_BLOCK(1),
           _HBARS_BALANCED_BLOCK(2)),
        re.DOTALL)

    ATTR_AND_VALUE = re.compile(
        r'(?P<attr>%(attr)s)\s*=\s*'
        r"(?:'(?P<lita>(?:%(hbv)s|%(hbb1)s|[^\'])*)'"    # LITA
        r'|"(?P<lit>(?:%(hbv)s|%(hbb2)s|[^\"])*)"'       # LIT
        r'|(?P<bare>(?:%(hbv)s|%(hbb3)s|[^\'\">\s])+)'   # bare value
        r')(?:\s+|(?=>|/>|%(hbb4)s))'           # end
        % {'attr': HtmlLexer._ATTR_NAME, 'hbv': _HBARS_VAR,
           'hbb1': _HBARS_BALANCED_BLOCK(1), 'hbb2': _HBARS_BALANCED_BLOCK(2),
           'hbb3': _HBARS_BALANCED_BLOCK(3), 'hbb4': _HBARS_BALANCED_BLOCK(4)},
        re.DOTALL)

    def parse_entity(self, i):
        # First, see if it's an html entity.
        retval = HtmlLexer.parse_entity(self, i)
        if retval is not None:
            return retval

        # OK, maybe it's a handlebars entity.
        # 3rd value here is true for tags that break up natural language runs.
        tags = (('{{!', '}}', True),
                # We never try to i18nize already-marked-up text.
                ('{{#_}}', '{{/_}}', True),
                ('{{_', '}}', True),
                ('{{', '}}', True),

                ('{{#ngettext', '{{/ngettext}}', True),
                ('{{#i18nDoNotTranslate', '{{/i18nDoNotTranslate}}', True),
                # TODO(csilvers): how to i18n-ize function args?  Not
                # critical since we don't seem to have any nl text here.
                ('{{#', '}}', True),
                ('{{else', '}}', True),
                ('{{/', '}}', True),
                ('{{>', '}}', True),
                # This must come before '{{', so it matches first.
                ('{{{', '}}}', False)
                # ('{{', '}}', False))
                )

        rawdata = self.rawdata
        for (starttag, endtag, segment_separates_nltext) in tags:
            if rawdata.startswith(starttag, i):
                entity_end = self.parse_to(endtag, i)
                self._call_callback(rawdata[i:entity_end],
                                    segment_separates_nltext)
                return self.updatepos(i, entity_end)

        # This is probably impossible: if we get here, it means
        # self.INTERESTING matched something not present in `tags`.
        return None

class NullTextHandler(object):
    """An object that collects html text segments and spits them out unchanged.

    This is used to test HtmlLexer.  It does quite a bit of processing
    of its input: storing each segment passed to it according to
    whether it breaks up a run of natural-language text or continues
    one, chopping off non-text at the beginning and end of each
    nl-text run, etc.  But then, it basically throws all that work
    away, taking each processed segment and just concatenating them
    together.

    However, a subclass could easily do something more interesting
    with each processed segment.

    NullTextHandler.handle_entity() is an appropriate function to pass
    in to HtmlLexer or its subclasses.
    """
    # Used to test if a run of natural language actually has any
    # *language* in it.  For instance, ' ' is a run of natural
    # language as far as HtmlLexer is concerned, but not an
    # interesting one as far as we're concerned...
    NO_NATURAL_LANGUAGE = re.compile(r'^([^\w&]|&(%s)\b)*$'
                                     % _NON_ALNUM_ENTITIES, re.UNICODE)

    # We also want to ignore most natural language (especially
    # whitespace) at the start of a run of text.
    NO_NATURAL_LANGUAGE_PREFIX = re.compile(r'^([^\w&]|&(%s)\b)+'
                                            % _NON_ALNUM_ENTITIES, re.UNICODE)

    # Likewise, we want to ignore whitespace/etc at the end of a run of
    # text.  But we want to keep periods and other closing punctuation.
    NO_NATURAL_LANGUAGE_SUFFIX = re.compile(r'(\s|[@#$^*|]|&(%s)\b)+$'
                                            % _NON_ALNUM_ENTITIES, re.UNICODE)

    def __init__(self, tag_parser):
        """tag_parser is used to i18nize nl-text inside tag attributes."""
        self.nltext_segments = []
        self.tag_parser = tag_parser

    def is_entity(self, segment):
        """Return true if segment is an , not a string of text."""
        return segment.startswith('<')

    def no_natural_language_or_already_translated(self, segment):
        """Return true if a text-segment has no nl-text needing translation.

        While s may have been deemed to be a 'natural language
        segment' by HtmlLexer, that doesn't mean it has any actual
        natural language text in it: it could be a run of whitespace
        or whatever.  Or it could already be marked for translation.
        This checks whether it's actually a segment with language in
        it.

        We say that html tags have no nl-text by default, and neither
        do text-segments with just punctuation or whitespace.  Subclasses
        can override this to add their own checks.
        """
        return (self.is_entity(segment) or
                self.NO_NATURAL_LANGUAGE.match(segment))

    def add_underscore(self, segments):
        return ''.join(segments)

    def inside_tag(self):
        """If we don't have someone else to parse tags, then we're doing it."""
        return self.tag_parser is None

    def _segment_is_tag(self, segment, tagname):
        return (segment == '<%s>' % tagname or
                segment.startswith('<%s ' % tagname))

    def replace_natural_language_in_attributes(self, tag_contents, tagname,
                                               attrs, value_poses, munge_fn):
        """Replaces any nl attrs with the result of the munge_fn within tag."""

        # This yields the attrs backwards, so when we rewrite one,
        # it doesn't affect the positions of the next ones.
        for i in natural_language_attributes(tagname, attrs):
            (_, val) = attrs[i]
            munged_val = munge_fn(val)
            value_pos = value_poses[i]     # (start, end) pos of value
            tag_contents = (tag_contents[:value_pos[0]] + munged_val +
                            tag_contents[value_pos[1]:])

        return tag_contents

    def handle_tag(self, tag_contents):
        """Return a version of tag_contents with nl-text in attrs marked up."""

        if not self.tag_parser:
            return tag_contents

        # First, we call into the parser to get the tagname and attributes.
        try:
            (tag_end, tagname, attrs, value_poses) = (
                self.tag_parser.parse_starttag(tag_contents, 0))
            assert tag_end == len(tag_contents)
        except AssertionError:        # not actually a tag
            return tag_contents

        # Check whether it's possible this tag has natural-language attr-vals.
        if not attrs:
            return tag_contents

        # i18nize any nl attrs within the tag.
        tag_contents = self.replace_natural_language_in_attributes(
            tag_contents, tagname, attrs, value_poses, self.tag_parser.parse)

        return tag_contents

    def add_i18n(self, text_segments):
        """Adds _() and other appropriate i18n markup to text_segments.

        At its simplest, this routine could be just:
           return self.add_underscore(text_segments)

        But we complexify this routine to handle leading and trailing
        non-language elements.  For instance, if text_segments is
        ['   hi   '], we want to do
           return '   ' + self.add_underscore(['hi']) + '   '
        so the translators don't have to translate whitespace.
        Likewise, if text_segments is ['', 'hi', ''].

        We do this by identifying leading and trailing segments that
        have no actual language in them ('like '' or ' ').  Then,
        we *also* identify the prefix and suffix of the remaining text
        that has no actual language in it (the ' hi ' example above).
        Once we've stripped those all off, we call add_underscore() on
        the middle that remains.

        Arguments:
           text_segments: a list of strings, each of which is a single
              entity as returned by the html parser (so a tag, a
              string of text, a jinja2 entity, etc).  We are promised
              that no entity in this list is something that separates
              natural-language runs (such as a '
' tag), and none
              is inside

redpajama_set_name: RedPajamaGithub