There are a couple of issues with your example:
1. You are including a copy of the named hook declaration within your (replace:) macro's associated hook, when all you should have in there is the replacement content.
<!-- Invalid -->
(replace: ?budgetStat)[|budgetStat>[$budget]]
<!-- Should be -->
(replace: ?budgetStat)[$budget]
2. You are adding invalid white-space or line-breaks between your macros and thier associated hooks, which the Harlowe TwineScript parser is automatically fixing for you. It's not a good idea to rely on it doing that correctly.
<!-- Invalid line-break placement. -->
(if: $budget < (cost of $posters) or $marketing contains "posters")
[ ... ]
<!-- Invalid white-space placement -->
(else:) [ ... ]
<!-- The above should be.. -->
(if: $budget < (cost of $posters) or $marketing contains "posters")[ ... ]
(else:)[ ... ]
Background info:
a. The non-interative content of a Passage is procressed just before that passage is shown, after which it is ignored until the next time the passage is shown again.
b. Dynamically changing the value of a variable referenced in the conditional expressions of a (if:) macro (after the passage has been shown) does not cause that macro to be re-evaluated again.
This means the conditional expressions of your (if:) macros are only being evaluated at the time the passage was shown. If you want them to be re-evaluated then you need to dynamically add them to the passage again, and one method you can use to do this is to move that content to a child passage and then inject that child passage into the parent each time you want it to be executed.
The following is a modified version of your original example, it includes fixes to the above issues as well as demostrates how to dynamically executed content as needed.
You have a choice on how to spend your marketing budget of £|budgetStat>[$budget].
|options>[(display: "Marketing Options")]
Your remaining budget is: £|budgetStat>[$budget] and your current marketing is |marketingStat>[$marketing].
... you will notice I have added a new options named hook to contain the contents of a new child passage.
The new Marketing Options child passage contains all the condition options content.
{
Posters £(print: cost of $posters):
(if: $budget < (cost of $posters) or $marketing contains "posters")[
You either already have or can't afford this item.
]
(else:)[
(link: "Buy")[
(set: $budget to it - (cost of $posters))
(set: $marketing to it + (a: "posters"))
(replace: ?budgetStat)[$budget]
(replace: ?marketingStat)[$marketing]
(replace: ?options)[(display: "Marketing Options")]
]
]
<br><br>
Flyers £(print: cost of $flyers):
(if: $budget < (cost of $flyers) or $marketing contains "flyers")[
You either already have or can't afford this item.
]
(else:)[
(link: "Buy")[
(set: $budget to it - (cost of $flyers))
(set: $marketing to it + (a: "flyers"))
(replace: ?budgetStat)[$budget]
(replace: ?marketingStat)[$marketing]
(replace: ?options)[(display: "Marketing Options")]
]
]
<br><br>
Socmed £(print: cost of $socmed):
(if: $budget < (cost of $socmed) or $marketing contains "socmed")[
You either already have or can't afford this item.
]
(else:)[
(link: "Buy")[
(set: $budget to it - (cost of $socmed))
(set: $marketing to it + (a: "socmed"))
(replace: ?budgetStat)[|budgetStat>[$budget]]
(replace: ?marketingStat)[$marketing]
(replace: ?options)[(display: "Marketing Options")]
]
]
}
... the above uses:
1. Collapsing Whitespace markup combined with HTML line-breaks (br elements) to control the layout of the options.
2. A (replace: ?options)[(display: "Marketing Options")] call to cause the options to be re-displayed, which in turn causes the (if:) macros to be re-evaluated.
I also restructed and re-worded your original text, and added the cost of the items so that the reader has some idea about the ramifications of their selections.