Hello,
The reason it works for the main product but not for the variants: your code prints $this->element->intro_txt once, server side, for the main product only. On the page HikaShop updates the variant values in the browser by swapping a set of hidden blocks it generates itself (name, code, price, description, and so on). Your hand made block is not one of those blocks, so it always keeps the main product value.
The fix is to reproduce that same swap for your field, entirely in template overrides. Three small edits:
1) In your product layout override, wrap your block in a container HikaShop can target, and use a class instead of the id on the inner div (the id would otherwise be duplicated once per variant):
<div id="hikashop_product_intro_txt_main"><?php
if(!empty($this->element->intro_txt))
echo '<div class="border rounded bg-light p-3 my-3">'.$this->element->intro_txt.'</div>';
?></div>
2) Override show_variants.php and add, next to the other per variant hidden blocks, a source block for your field:
<div id="hikashop_product_intro_txt_<?php echo $variant_name; ?>" style="display:none;"><?php
if(!empty($variant->intro_txt))
echo '<div class="border rounded bg-light p-3 my-3">'.$variant->intro_txt.'</div>';
?></div>
3) Add this small script once (for example at the bottom of your product layout override). It copies the selected variant's block into the visible one each time a variant is chosen:
<script>
(function(){
if(!window.Oby || !Oby.registerAjax) return;
Oby.registerAjax(['hkContentChanged'], function(p){
var src = document.getElementById('hikashop_product_intro_txt' + p.selection);
var main = document.getElementById('hikashop_product_intro_txt_main');
if(main && src) main.innerHTML = src.innerHTML;
});
})();
</script>
That reproduces exactly how HikaShop swaps its built in fields (name, price, description) for your custom field, so your box updates as the customer changes the variant.
One note: your custom field is a field on the product table, so it is a real column, and each variant is a product row that carries its own value. That is why $variant->intro_txt is available in show_variants.php the same way $this->element->intro_txt is on the main product.