Query specific file field form page

Situation:
I have a page called Work which shows a list of published subpages called projects, and I would like the preview image for each project page to be the first image or video poster from the a file fields called MediaGallery that is located in the project blueprint.

Question:
Can this be achieved with a Query in the blueprint or should it be solved with a page model?

See screenshot:

The published project pages shown on the Work page comes from section/project.yml, where I have some far been able to feature an image with the following query:

query: page.images.sortBy('sort').first

Full blueprint for section/project.yml is:

type: pages
label: Projects
layout: cards
size: large
template: project
image:
  query: page.images.sortBy('sort').first
  ratio: 5/4
  cover: true
  back: white
info: "{{ page.tags }}"

But this does not pickup the first file from the specific MediaGallery files field I have on the project blueprint but rather picks up the first image on another file fields called imageMedia.

The full blueprint for the project.yml looks as following:

#
# Blueprint for Projects
#
# page title
title: Project

# options for when creating a new work projects
create:
  title: "{{ page.client }} – {{ page.project }}"
  fields:
    - client
    - project
  slug: "{{ page.client }}-{{ page.project }}"

# options for available status options
status:
  listed: Published
  draft: Draft
  unlisted: Unlisted

#
# Content
#
tabs:
  #
  # TAB 1 - content
  #
  content:
    label: Content
    icon: text
    columns:
      # layout
      media:
        width: 1/2
        fields:
          #
          # Project - media
          #
          mediaGallery:
            type: files
            layout: cards
            size: medium
            label: Project Media
            uploads: false
            help: select media, to upload see storage tab.
            sortable: true
            info: "{{ file.template }}"
      #
      # Project - information
      #
      information:
        width: 1/2
        fields:
          #
          # Project - client
          #
          client:
            label: Project client
            width: 1/2
            type: select
            required: true
            placeholder: select client
            help: Select client for project, or create new under Work > Settings
            options:
              type: query
              query: site.find("work").clients.toStructure
              text: "{{ item.clientname }}"
              value: "{{ item.clientname }}"
          #
          # Project - title
          #
          project:
            label: project title
            width: 1/2
            type: text
            help: Enter the project name
            required: true
          # break
          lineA:
            type: line
          #
          # Project - categories
          #
          tags:
            type: tags
            label: Categories
            min: 1
            accept: options
            help: Select one or more categories for the project, or create new under Work > Settings
            options:
              type: query
              query: site.find("work").categories.toStructure
              text: "{{ item.category }}"
              value: "{{ item.category }}"
          # break
          lineB:
            type: line
          #
          # Project - description
          #
          description:
            type: textarea
            size: small
            buttons: false
          toggleDisplayDescription:
            label: Hide description on project page?
            type: toggle
            help: Hide the description on the project page but will keep it for SEO.
            default: "no"
            options:
              - no
              - yes
          # break
          lineC:
            type: line
          #
          # Project - credit list
          #
          credits:
            label: Credit list
            type: structure
            fields:
              name:
                label: name
                type: text
              role:
                label: role
                type: text
              link:
                label: link
                type: url

  #
  # TAB 2 - media storage
  #
  storage:
    label: Media
    icon: import
    fields:
      #
      # Media - Image uploading section
      #
      imageMedia:
        label: Image upload
        help: Upload project images here.
        type: files
        layout: cards
        size: medium
        uploads: image
        info: "{{ file.template }}"
      # break
      lineD:
        type: line
      #
      # Media - Video uploading section
      #
      videoMedia:
        label: Video upload
        help: Upload project videos here.
        type: files
        layout: cards
        size: medium
        uploads: video
        info: "{{ file.template }}"
      # break
      lineF:
        type: line
      #
      # Media - Audio uploading section
      #
      audioMedia:
        label: Audio upload
        help: Upload project audio here.
        type: files
        layout: cards
        size: medium
        uploads: audio
        info: "{{ file.template }}"

The reason I have it set up as such is that I needed to hide a video poster image while having a sortable gallery as discussed in this thread:

The first Tab contains another Files Filed called MediaGallery which allow me to select the images, video and audio from the project and sort them to my liking, before being featured on the frontend.

That means the project page’s MediaGallery can have as it’s first file either a video, image or audio file which I would like to query so I can use it as a project preview on the Work page.

Here is my video blueprint where I upload an image:

title: Video

accept:
  type: video

image:
  type: query
  query: file.videoCoverPhoto.toFile
  ratio: 5/4
  cover: true

columns:
  - width: 1/3
    sections:
      information:
        type: fields
        fields:
          videoDescription:
            label: Video description
            type: text
          caption:
            label: Video caption
            type: textarea
            buttons: false
            placeholder: caption for video
          videoCoverPhoto:
            type: files
            layout: list
            label: Video poster
            size: tiny
            uploads:
              template: poster
            max: 1

  - width: 1/3
    sections:
      meta:
        type: fields
        fields:
          license:
            label: License
            help: Include license meta data for the image.
            width: 1/2
            type: toggle
            default: true
            text:
              - "no"
              - "yes"
          copyright:
            label: Copyright
            help: Include copyright meta data for the image.
            width: 1/2
            type: toggle
            default: true
            text:
              - "no"
              - "yes"
          videoDurationHours:
            label: "Hours:"
            help: "hours"
            width: 1/3
            type: time
            display: HH
          videoDurationMinutes:
            label: "Minutes:"
            help: ""
            width: 1/3
            type: time
            display: mm
          videoDurationSeconds:
            label: "Seconds:"
            help: ""
            width: 1/3
            type: time
            display: ss
          videoUploadDate:
            label: upload date
            type: date
          videoWidth:
            label: video width
            type: number
            width: 1/2
          videoHeight:
            label: video height
            type: number
            width: 1/2
          toggleLoopable:
            label: Is the video loopable?
            type: toggle
            default: true
            text:
              - "no"
              - "yes"
  - width: 1/3
    sections:
      color:
        type: fields
        fields:
          videoForgroundColor:
            type: color
            label: Image forground color
            help: Pick a destinct forground color from the image
          videoBackgroundColorFrom:
            type: color
            label: Image background color
            help: Pick a destinct background color from the image
          videoBackgroundColorTo:
            type: color
            label: Image background color
            help: Pick a destinct background color from the image

Any suggestions or hints would be greatly appreciated.

Kirby: 4.1.0
PHP: 8.3.3
Browser: Safari 17.3
OS: Mac 14.3

Managed to solve it with a custom page model.
The solution is as following:

<?php

use Kirby\Cms\Page;
use Kirby\Cms\Files;

class ProjectPage extends Page
{
    public function getProjectPreview()
    {
        $projectPages = page("work")->children();

        foreach ($projectPages as $page) {
            $firstFileInMediaGallery = $this->mediagallery()
                ->toFiles()
                ->first();

            if ($firstFileInMediaGallery->type() === "image") {
                return $firstFileInMediaGallery;
            } elseif ($firstFileInMediaGallery->type() === "video") {
                $videoPoster = $firstFileInMediaGallery
                    ->files()
                    ->filterBy("template", "poster");
                return $videoPoster->first();
            }
        }
    }
}