Actual Templates
costumes (approximated by example A)
{embed="site/.part-header" title="Costumes"}
<h1>Costumes</h1>
<div class="category-nav">
<ul>
<li class="previous-concept"><a href="" class="disabled">Previous Concept</a></li>
<li class="back-to-grid"><a href="" class="disabled">Back to Grid</a></li>
{exp:channel:categories channel="costumes" style="linear" parent_only="yes"}
{if count == "1"}
{exp:channel:entries category="{category_id}" orderby="title" sort="asc" limit="1"}
<li class="next-concept"><a href="{site_url}costume-detail/{entry_id}/">Next Concept</a></li>
{/exp:channel:entries}
{/if}
{/exp:channel:categories}
</ul>
<br class="clear" />
</div>
<div class="costume-grid">
{exp:query sql="SELECT cat_name as parent_category_name, cat_id as parent_category_id
FROM exp_categories
WHERE group_id = 4
AND parent_id = 0
ORDER BY cat_order ASC"}
<div class="costume-grid-row{if count == total_results} last{/if}">
<div class="gridbox">
<p class="costumecategory">{parent_category_name}</p>
</div>
{embed="site/.part-category-item" category_id="{parent_category_id}" base_url="{site_url}costume-detail/"}
<br class="clear" />
</div>
{/exp:query}
</div>
<div class="intros">
<div class="graybox">
{exp:channel:entries channel="static" entry_id="11" limit="1"}
<h2>{title}</h2>
{content-1}
{/exp:channel:entries}
<div class="right">
{exp:channel:entries channel="costumes" dynamic="no" category="60" limit="1" sort="asc"}
<a id="explore" class="text-button" href="{site_url}costume-detail/{url_title}/">Explore</a>
{/exp:channel:entries}
</div>
</div>
</div>
{embed="site/.part-footer"}
.part-category-item
{exp:query sql="SELECT c.cat_id, c.cat_url_title, c.cat_name
FROM exp_channel_titles t
LEFT JOIN exp_channel_data d ON t.entry_id = d.entry_id
LEFT JOIN exp_category_posts cp ON t.entry_id = cp.entry_id
LEFT JOIN exp_categories c ON cp.cat_id = c.cat_id
WHERE c.parent_id = '{embed:category_id}'
ORDER BY c.cat_order ASC"}
{exp:channel:entries category="{cat_id}" orderby="title" sort="asc" dynamic="no"}
<div class="gridbox" style="overflow: visible;">
{categories show="not {embed:category_id}" limit="1"}
<table cellspacing="0" cellpadding="0" style="overflow: visible;">
<tr style="overflow: visible;">
<td style="overflow: visible;">
<a href="{embed:base_url}{url_title}/" >
<img src="{category_image}" style="margin: 0; padding: 0;" title="{title}" />
</a>
</td>
</tr>
</table>
<p><a href="{embed:base_url}{url_title}/">{category_name}</a></p>
{/categories}
</div>
{/exp:channel:entries}
{/exp:query}
.part-category-item-test (identical to previous, except for filename)
{exp:query sql="SELECT c.cat_id, c.cat_url_title, c.cat_name
FROM exp_channel_titles t
LEFT JOIN exp_channel_data d ON t.entry_id = d.entry_id
LEFT JOIN exp_category_posts cp ON t.entry_id = cp.entry_id
LEFT JOIN exp_categories c ON cp.cat_id = c.cat_id
WHERE c.parent_id = '{embed:category_id}'
ORDER BY c.cat_order ASC"}
{exp:channel:entries category="{cat_id}" orderby="title" sort="asc" dynamic="no"}
<div class="gridbox" style="overflow: visible;">
{categories show="not {embed:category_id}" limit="1"}
<table cellspacing="0" cellpadding="0" style="overflow: visible;">
<tr style="overflow: visible;">
<td style="overflow: visible;">
<a href="{embed:base_url}{url_title}/" >
<img src="{category_image}" style="margin: 0; padding: 0;" title="{title}" />
</a>
</td>
</tr>
</table>
<p><a href="{embed:base_url}{url_title}/">{category_name}</a></p>
{/categories}
</div>
{/exp:channel:entries}
{/exp:query}
Update: I've now added these templates just in case there's something else to be gleaned, but deleting the original .part-category-item and renaming the (identical) .part-category-item-test worked. At this point I assume it's impossible to narrow down to the answer without also sharing the database, asking the person who migrated the site exactly what he did, etc. I'll chalk it up to some kind of storage or encoding issue, but that's still just a guess.
I can't create and accept my own answer because I don't think this constitutes one – just a fix. I'm fine with Stack Overflow but end up in messy situations like this on the EE Stack Exchange. Open to suggestions!