diff --git a/src/crop/farmplot.jl b/src/crop/farmplot.jl
index 596da80ea50f0db4bba5bdbe50da85270f99b236..359c0c00d53ac50f5614f8cd71f8f3a7faf5ffad 100644
--- a/src/crop/farmplot.jl
+++ b/src/crop/farmplot.jl
@@ -192,23 +192,23 @@ end
 """
     cropheight(model, position)
 
-Return the height of the crop at this position, or nothing if there is no crop here
+Return the height of the crop at this position, or 0 if there is no crop here
 (utility wrapper).
 """
 function cropheight(pos::Tuple{Int64,Int64}, model::SimulationModel)
-    ismissing(model.landscape[pos...].fieldid) ? 0cm : #FIXME should not return 0
-              model.farmplots[model.landscape[pos...].fieldid].height
+    ismissing(model.landscape[pos...].fieldid) ? 0cm : #FIXME can I return something better than 0?
+              model.farmplots[model.landscape[pos...].fieldid].height*1cm #FIXME units
 end
 
 """
     cropcover(model, position)
 
-Return the percentage ground cover of the crop at this position, or nothing if there is no crop
+Return the percentage ground cover of the crop at this position, or 0 if there is no crop
 here (utility wrapper).
 """
 function cropcover(pos::Tuple{Int64,Int64}, model::SimulationModel)
     #FIXME LAItotal != ground cover?
-    ismissing(model.landscape[pos...].fieldid) ? 0 : #FIXME should not return 0
+    ismissing(model.landscape[pos...].fieldid) ? 0 : #FIXME can I return something better than 0?
               model.farmplots[model.landscape[pos...].fieldid].LAItotal
 end
 
diff --git a/src/nature/species/skylark.jl b/src/nature/species/skylark.jl
index 606ebc478ecad982421ba6d38cdbbd0218251cb0..6da29352cc3eed3f90ee3e2d67396db9bff86542 100644
--- a/src/nature/species/skylark.jl
+++ b/src/nature/species/skylark.jl
@@ -35,13 +35,12 @@ At the moment, this implementation is still in development.
     const visionrange::Length = 200m #XXX arbitrary
     
     const eggtime::Int64 = 11 # days from laying to hatching
-    const nestlingtime::UnitRange{Int64} = 7:11 # days from hatching to leaving nest
-    const fledglingtime::UnitRange{Int64} = 25:30 # days from hatching to independence
+    const nestlingtime::Int64 = 9 # days from hatching to leaving nest
+    const fledglingtime::Int64 = 21 # days from leaving the nest to independence
 
+    #XXX predation mortality should be habitat-dependent
     const eggpredationmortality::Float64 = 0.03 # per-day egg mortality from predation
-    const nestharvestmortality::Float64 = 1.0 # egg/nestling mortality after a harvest event
     const nestlingpredationmortality::Float64 = 0.03 # per-day nestling mortality from predation
-    const fledglingharvestmortality::Float64 = 0.5 # fledgling mortality after harvest
     const fledglingpredationmortality::Float64 = 0.01 # per-day fledgling mortality from predation
     const firstyearmortality::Float64 = 0.38 # total mortality in the first year after independence
     const migrationmortality::Float64 = 0.33 # chance of dying during the winter
@@ -57,17 +56,14 @@ At the moment, this implementation is still in development.
     const nestingbegin::Tuple{Int64,Int64} = (April, 10) # begin nesting in the middle of April
     const nestbuildingtime::UnitRange{Int64} = 4:5 # 4-5 days needed to build a nest (doubled for first nest)
     const eggsperclutch::UnitRange{Int64} = 2:5 # eggs laid per clutch
-    const breedingdelay::Int64 = 18 # days after hatching before starting a new brood #XXX ??
     const nestingend::Int64 = July # last month of nesting
 
     # individual variables
-    #FIXME check what needs to be rewritten
-    timer::Int64 = 0 # a count-down timer that can be used for different purposes
+    timer::Int64 = 0 # a counter that can be used for different purposes
     migrationdates::Tuple = () # is defined by each individual in @create(Skylark)
     mate::Int64 = -1 # the agent ID of the mate (-1 if none)
     nest::Tuple = () # coordinates of current nest
-    nestcompletion::Int64 = 0 # days left until the nest is built
-    clutch::Int64 = 0 # number of offspring in current clutch
+    clutch::Int64 = 0 # number and life stage of offspring in current clutch
 end
 
 
@@ -203,8 +199,8 @@ Females that have found a partner build a nest and lay eggs in a suitable locati
         self.timer -= 1
     end
     # tillage and harvest destroys the nest
-    @respond(tillage, self.nest = ())
-    @respond(harvesting, self.nest = ())
+    @respond(tillage, destroynest!(self, "tillage"))
+    @respond(harvesting, destroynest!(self, "harvesting"))
 end
 
 """
@@ -212,12 +208,32 @@ Females that have laid eggs take care of their chicks, restarting the nesting pr
 chicks are independent or in case of brood loss.
 """
 @phase Skylark breeding begin
-    #TODO wait for eggs to hatch & chicks to mature, checking for mortality
-    # restart breeding cycle if there is time
-    if self.clutch == 0 && month(model.date) <= self.nestingend
-        @setphase(nesting) #TODO breeding delay?
-    elseif month(model.date) > self.nestingend
-        @setphase(nonbreeding)
+    #XXX Schachtelbruten - sometimes skylarks start a new nest before the previous young are gone
+    # wait for eggs to hatch & chicks to mature, checking for predation and disturbance mortality
+    self.timer += 1
+    #XXX this should be habitat-dependent!
+    if self.timer <= self.eggtime
+        @chance(self.eggpredationmortality) && destroynest!(self, "predation")
+    elseif self.timer <= self.eggtime + self.nestlingtime
+        @chance(self.nestlingpredationmortality) && destroynest!(self, "predation")
+    elseif self.timer <= self.eggtime + self.nestlingtime + self.fledglingtime
+        @chance(self.fledglingpredationmortality) && destroynest!(self, "predation")
+    else
+        # create new young, reset timer and clutch counter
+        @reproduce(self.clutch, self.mate)
+        self.clutch = 0
+    end
+    if self.clutch > 0
+        # tillage and harvest destroys the nest
+        @respond(tillage, destroynest!(self, "tillage"))
+        @respond(harvesting, destroynest!(self, "harvesting"))        
+    else # restart breeding cycle if there is time
+        self.timer = 0
+        if month(model.date) <= self.nestingend
+            @setphase(nesting)
+        elseif month(model.date) > self.nestingend
+            @setphase(nonbreeding)
+        end
     end
 end
 
@@ -236,8 +252,8 @@ function migrationdates(skylark::Skylark, model::SimulationModel)
     minarrive = skylark.sex == male ? (February, 15) : (March, 1)
     deltaleave = @rand(0:45) #XXX ought to be normally distributed
     deltaarrive = @rand(0:15) #XXX ought to be normally distributed
-    leave = Date(year(model.date), minleave[1], minleave[2]) + Day(deltaleave))
-    arrive = Date(year(model.date)+1, minarrive[1], minarrive[2]) + Day(deltaarrive))
+    leave = Date(year(model.date), minleave[1], minleave[2]) + Day(deltaleave)
+    arrive = Date(year(model.date)+1, minarrive[1], minarrive[2]) + Day(deltaarrive)
     (leave, arrive)
 end
 
@@ -292,7 +308,9 @@ end
     foragequality(skylark, model, pos)
 
 Calculate the relative quality of the habitat at this position for foraging.
-(Approximated from Püttmanns et al., 2021; Jeromin, 2002; Jenny, 1990b.)
+This assumes that open habitat is best (quality = 1.0), and steadily decreases as vegetation
+height and/or cover increase. (Linear regressions based on Püttmanns et al., 2021; Jeromin, 2002;
+Jenny, 1990b.)
 """
 function foragequality(skylark::Skylark, model::SimulationModel, pos::Tuple{Int64,Int64})
     #TODO this is a key function that needs to be validated thoroughly
@@ -301,18 +319,9 @@ function foragequality(skylark::Skylark, model::SimulationModel, pos::Tuple{Int6
         (@distanceto(builtup) < skylark.mindistancetoedge)
         return 0.0
     end
-    quality = 1.0
-    f = farmplot(pos, model)
-    # Assume that grass and soil have a habitat quality of 1.0. For fields, calculate quality
-    # as the sum of cover quality and height quality (each modelled using a linear regression).
-    if !isnothing(f)
-        groundcoverfactor = x -> bounds((-1/skylark.maxforagecover)*x + 1.0, max=1.0)
-        plantheightfactor = x -> bounds((-1/skylark.maxforageheight)*(x |> cm) + 1.0, max=1.0)
-        #FIXME need percentage cover in FarmPlot, not LAI
-        #FIXME height is currently dimensionless in FarmPlot, hence the conversion to cm
-        quality = bounds(groundcoverfactor(f.LAItotal) + plantheightfactor(f.height*1cm), max=1.0)
-    end
-    return quality
+    groundcoverfactor = x -> bounds((-1/skylark.maxforagecover)*x + 1.0, max=1.0)
+    plantheightfactor = x -> bounds((-1/skylark.maxforageheight)*(x |> cm) + 1.0, max=1.0)
+    return bounds(groundcoverfactor(@cropcover()) + plantheightfactor(@cropheight()), max=1.0)
 end
 
 """
@@ -324,12 +333,24 @@ function allowsnesting(skylark::Skylark, model::SimulationModel, pos::Tuple{Int6
     #TODO is this condition correct? -> needs validation!
     (@landcover() == grass ||
      (@landcover() == agriculture &&
-      (skylark.nestingheight[1] <= @cropheight() <= skylark.nestingheight[2]) &&
+      (skylark.nestingheight[1] <= (@cropheight()) <= skylark.nestingheight[2]) &&
       (skylark.nestingcover[1] <= @cropcover() <= skylark.nestingcover[2]))) #&&
+    #FIXME if we add the distance requirement, females don't find a nesting spot?
       #(@distanceto(forest) < skylark.mindistancetoedge) &&
       #(@distanceto(builtup) < skylark.mindistancetoedge)
 end
 
+"""
+    destroynest!(skylark, reason)
+
+Remove the skylark's nest and offspring due to disturbance or predation.
+"""
+function destroynest!(self::Skylark, reason::String)
+    self.nest = ()
+    self.clutch = 0
+    @debug("$(animalid(self)) had her nest destroyed by $reason.")
+end
+
 ## INITIALISATION
 
 """
@@ -345,7 +366,7 @@ should currently be on migration. Also sets other individual-specific variables.
             @setphase(territorysearch) :
             @setphase(matesearch)
         @migrate(arrive)
-        self.migrationdates = self.migrationdates .+ Year(1))
+        self.migrationdates = self.migrationdates .+ Year(1)
     end
 end