The two best starting points are the openhab docs and to spend some time just search and browsing these forums. You’ll want to decide which scripting language to focus on. The three most common options are the Rules DSL, which many people find easy to start with as it is fairly readable, but doesn’t have quite as much functionality. Other common options are python, or javascript (groovy is available but has some of the least community support as far as I can tell). JS and python are far more powerful (but as a result a little more in depth). You’ll find many posts of the various scripting language options discussing pros and cons. You’ll also find many posts that are just examples of common script actions and hints about efficient ways to do things and links to helper libraries that other users have created to make certain common tasks less cumbersome.
If you really want to stick with the visual building block technique you will find posts here about how to connect node-red up to openhab and create node-red flows with far more complexity than what is available through blocky.
Blocky, however, could be a very valuable resource, if you decide to try javascript. When you make a blocky script, there’s a little blue button
in the bottom right-hand corner. Pushing that will show you the underlying javascript that corresponds to your blocky code and this is a very good way to start to get comfortable with the basics of javascript in OH.

becomes
var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID);
if (itemRegistry.getItem('MyItem').getState() == 'Some State') {
logger.info(('My item just changed to: ' + String(itemRegistry.getItem('MyItem').getState())));
}
Obviously, you can only learn the js that has corresponding blocky blocks at the moment, but that’s a pretty solid foundation.
I have precisely this functionality enabled in my system. I use a timer for this. There is a mountain of information on timers for all of the different languages here in the forums, but the basic idea is that when you create a timer you give it a set duration and some commands. When you start it up it waits for that duration and then executes the commands.
In my case, I have a function that checks if my light is already at full brightness. If not, it then increases the brightness by 5% and creates timer with a command to run the same function again after 30 seconds. So, when my alarm goes off, the function gets called for the first time and then every 30s it gets called again until the light is at full brightness.
Delays/Wait/Sleep got a bad rep from OH2 because of thread handling. The issue isn’t as much of a problem with OH3 so a loop with a delay statement is also an acceptable solution in this case, particularly as it seems unlikely that instances of this rule might need to overlap.