Website2026-08-21

Trubači Stars Belgrade — trumpet orchestra site for weddings and celebrations

The client had two competing sites and was looking for a third one. Instead of the third domain, the existing two were measured — so one of them was emptied and built from scratch as a site only for Belgrade, with a page for each municipality.

Homepage of Trubači Stars Belgrade
Homepage - orchestra for weddings and all celebrations, with a phone number right next to the title
Client
Trubači Stars — Stefan Tenor's orchestra
Activity
Trumpet orchestra for weddings and celebrations
Area
Belgrade, 15 municipalities
Type
Multi-page site, 28 URLs
Technologies
PHP 8.3 · nginx · vanilla JS · JSON-LD
Site
trubaci-trubacizasvadbe.rs
Technology
PHP 8.3 · nginx · Vanilla JS · AVIF/WebP · JSON-LD
01

Problem and context

Who are Trubači Stars

Trubači Stars are a trumpet orchestra. Stefano Tenor. The orchestra is based in Leskovac and performs throughout Serbia — weddings, celebrations, baptisms, birthdays and other occasions traditionally celebrated with brass music. The orchestra lists fifteen years of work and over five hundred performances on its website.

The line-up is selected according to the space and occasion, from four to eight musicians: first and second trumpet, tenor horn, tuba and percussion. The line-up scales in that order — four musicians suit a family celebration and the smaller hall, six cover the full wedding program, eight a large venue and all-night celebrations. The ensemble performs acoustically, the trumpet does not need electricity, which is a practical advantage for the yard, church and garden. The full line-up and working process can be found on the page about the orchestra.

Belgrade is the city to which the orchestra travels most often. This is also the reason why this site exists — as a dedicated presentation of the orchestra’s work in Belgrade.

Section with occasions: weddings, celebrations, baptisms and birthdays
Weddings, celebrations, baptisms and birthdays - the wedding stays here, other occasions lead to the sister site
02

Why Belgrade got its own site

The client had two domains and asked for a third one, with the explanation that it would rank better that way. Instead of creating a third site, the existing two were measured first — through Search Console and through logs for fourteen days.

The measurement showed the opposite of what was expected. The wedding domain had about 20 organic clicks, and Google had no field speed data for it — Google's own confirmation that there aren't enough people coming to the site to even measure it. Another site ranked for wedding queries, Trubaci za slavja, not a domain specifically made for weddings.

The reason was visible as soon as it was compared. Sixty city pages on both domains had literally the same H1, and on the wedding page there were also pages about celebrations, baptisms and farewells — so the same content, the same intention, two domains. Google chose one and suppressed the other. A third domain would just repeat that error.

The proposal was different: separate intents, not duplicate domains. The sister site retains all of Serbia and all opportunities, and this one is being emptied and built from scratch as a Belgrade hub — one city, fifteen municipalities, all pages about Belgrade and nothing else. The client agreed, and it was more difficult than the construction itself: giving up the third site means admitting that the problem was not the lack of a domain.

Price section on the Trubači Stars Belgrade website
The price is known before the call - the part that is why the price list was written
03

How the site is structured

The site has 28 URLs. The homepage is the hub for Belgrade, below it are pages for fifteen municipalities, then wedding protocol in Belgrade, price list, two guides through selection and price, then the usual pages — gallery, repertoire, about us, contact, frequently asked questions — and two legal ones.

Municipality pages required the closest editorial review. Fifteen pages that differ only in the name of the municipality are can look to Google like one page repeated fifteen times. That's why each has its own settlements — Blokovi, Bežanija and Ledine for New Belgrade; Gardoš, Batajnica and Altina for Zemun; Kaludjerica, Leštane and Vinča for Grocka — typical occasions for that region and a specific details about travel to that area. Parking in the block in New Belgrade is not the same problem as the alley in Grocka, and that's what it says.

After writing, the actual similarity of the text between those pages was measured using five-word sequences: an average of about nine percent, a maximum of fourteen percent for the closest pair. This is a difference that can be seen by reading, not only in the table.

Lazarevac and Obrenovac deliberately do not have pages here — those two are maintained by a sister site and are already ranked there. It makes no sense to compete in a place where something works.

Trubači Stars Belgrade website on the phone
The same website on the phone
04

Starting prices before the call

On the price list there is an observation that was the reason for that part of the site: most Belgrade orchestras do not publish any figures. The decision was to publish what can be said with certainty — where the price starts and what moves it — and to say the final amount during the call, when the date, place and hour are known.

The first version had three packs priced per card, with the middle one highlighted. The owner withdrew them after a week, and he was right: the package promises to be a selection from the list, and he calculates the price based on the hourly rate, composition and date. Packages were deleted from the entire structure — from the homepage, from the price list, from all municipal pages and from the structured data — and only the lower limit and the explanation of what moves it up remained.

What has taken the place of the table is more useful than it. Cenovnik now has a block "What is included in the agreed amount" — arriving thirty to sixty minutes earlier, agreement on the repertoire and learning a song to order, own instruments, coordination of the hourly rate with the hall, groomsmen and photographer — and a block "What we guarantee". These are items that are charged separately elsewhere or appear only on the day of the celebration.

05

Call instead of form

The site does not have a contact form. It's not an oversight, but a decision by the client, made on the basis of data: the form on the old site did not receive a single query for fourteen days, and its log was a file of zero bytes. The deal is arranged over the phone, in one conversation, and the site is aligned with that instead of pretending it isn't.

That's why every call to action is a phone number, with Viber and WhatsApp on the same number, and another line at the bottom of each page. In order to know how many calls the site actually brings, clicking on the number sends a short message to the server via sendBeacon and is recorded in the call log. No cookies and no third-party measurement tools — one line in a file that rotates along with the rest of the site's logs.

Instead of a form, at the bottom of each page is the same block: say the date and place, you know the price immediately. It also says that for the season from May to October, appointments are booked two to three months in advance — information that prevents disappointment before it occurs.

06

Technical performance

The site is written in PHP 8.3, without any framework and without a content management system. Common parts are in includes/ — header, navigation, footer, booking block, and a few helper functions — so changing the navigation is done once.

That sounds obvious until compared to what was found. The old site on the same domain had eighty-three independent PHP files, without a single common part. Each change through the entire site was a script over eighty-three files, and each such pass was an opportunity for something to remain in two or three different forms. When this happened with structured data, the correction was a separate job in itself.

The code is deliberately small: one CSS file of about twenty-six kilobytes and one JavaScript file of about seven, pure vanilla, without any libraries. The icons are not a font, but an embedded SVG sprite that is rendered from <use> — FontAwesome from the old site was not transferred, and with it, its files of over two hundred kilobytes. Playfair Display and Poppins fonts are hosted from the same server, and only in weights that are actually used.

Three helper functions carry the whole system: one protects the output, the second assembles srcset from the existing versions of the image, and the third creates an FAQ schema from the same series of questions from which the visible text on the page is printed. The last one is the most important — it's the only reliable way to ensure that the structured data never diverges from what the visitor is reading.

Detail that cost time: that function cannot be called from the page itself. Pages define their FAQ before including the header where the function lives, so calling from a page causes a fatal error. Solved by having the page just describe the questions and the header turning them into a schema — one place, twenty-one pages.

07

Threadline and entrance animations

Through the entire site runs a thin gold line along the left edge, with a small diamond on each section. A line is rendered as it scrolls, and diamonds light up when a section enters the field of view. It is the only decoration on the site and it is intentionally the only one.

It's interesting how it's derived: the line doesn't use a single line of JavaScript. Drawing is done by CSS animation related to the scroll, inside the @supports query — browsers that don't support it get the same line, only static. The same applies to visitors who have turned off animations in the system.

Section entrance animations had a more expensive bug. A common pattern is to hide everything with a single class at <html> and reveal them in order — meaning that the element in the first screen is also waiting for a deferred script to be painted at all. On the price list it was eight hundred and thirty two milliseconds of pure waiting before the main title came on. Now only what's below the fold is hidden: the script on startup measures where each element is and assigns a class only to those off-screen, and the first screen is animated with pure CSS from the critical block.

Another castle from the same circle is worth noting. The trigger threshold was set as a percentage of the screen height, so on a large desktop window, the last line in the footer—the one with the attribution and legal links—falls entirely into the cutoff band and is never displayed. The phone worked fine, because the sticky tape with the phone number lifted it up. Hence the rule that remains: the line with authorship and legal links must never depend on JavaScript.

08

Speed

The cover in Google's mobile test on the first day was seventy-seven, with the highest draw at 3.7 seconds and a content displacement of 0.24. After fixes: hundred, draw 1.8 seconds, offset zero, block main thread ten milliseconds. Price list and verified municipality page also hundred.

The content move had one cause and it was instructive. The fifteen-link dropdown is only styled in the main CSS, which is loaded asynchronously, so by the time it arrives, it has rendered unfolded and pushed the entire content down. The rule that follows applies to every site: every dropdown must have a collapsed state in critical CSS, without exception.

The rest of the winnings came in order. Google's measurement script, one hundred and seventy kilobytes, has been switched from loading in the header to loading only after the page loads. The function that checks which sections entered the screen is separated into first all the measurements and only then the class entry — because each entry cancels the next measurement and the layout is calculated again, for each element. The scroll listener is not mounted at all when the browser supports IntersectionObserver.

Images go in AVIF and WebP, in several widths and with full srcset, with dimensions typed so they don't move content while loading. Eleven recordings in the gallery are not pulled by YouTube until clicked — until then there is only a thumbnail and a button. A gallery with eleven built-in players would be by far the slowest page on the site.

09

Structured data and visibility

The site carries MusicGroup for the orchestra, WebSite and Service on the homepage, LocalBusiness on each municipal page, and FAQPage on twenty one pages. Each municipal record is linked to an orchestra via a @id reference, rather than repeating it in its entirety — so there is one orchestra with fifteen areas, not fifteen orchestras.

The biggest inherited risk was not technical. Both domains were rated five out of five with one hundred and twenty-seven votes in the structured data — on one hundred and fifty-eight pages, with no review displayed to cover it and no profile to come from. It's a fictitious rating, Google treats it as spam in structured data, and the result is a manual penalty on the domain. It has been stripped from all pages, along with the visible rating claim, and replaced with a verifiable statement: a lineup of four to eight musicians.

It was the same with local data. Five municipality pages had an address in Leskovac with Belgrade geographic coordinates, in the same block — a contradiction in two adjacent lines. The orchestra in Belgrade does not have an address, so the address was removed instead of invented, and the area of operation remained.

Section over all twenty-eight pages after all: the canonical URLs match the sitemap on all twenty-eight, there are no duplicate titles, descriptions or H1s, all images have alternative text and dimensions, and every question and answer from the FAQ scheme exists in the visible text of the page. The site has llms.txt with the correct data, and robots.txt explicitly allows language model bots - search is increasingly happening there as well.

10

Redirects - so as not to lose what is already ranking

The transition was the most sensitive part of the job, because the old site on that domain was not empty. He had over sixty city pages that were, as it were, in the Google index. Deleting them means throwing away everything that domain has earned up until then.

The solution was a permanent redirect map in nginx. Thirteen wedding pages were previously translated to the sister site template and published there under the same servants, so the redirects go to them. Sixty-two city pages, the price page and the orchestra page also go to a sister site, to the same servant. The old address trubaci-beograd leads to the new address, because now it is that page.

One detail could have ruined everything. The sister site previously had five redirects in the opposite direction — from it to this domain. If they remained, we would get a loop in which the two servers redirect each other until the browser gives up, and that on the pages that brought the most visits. They were removed before the new map was even installed.

The entire old site was moved aside, not deleted, and still sits on the server along with a backup made before each step. If it turns out that something was missed, there is somewhere to take it out.

11

Outcome

What the client received

Two sites that no longer compete. Sestrinski holds the whole of Serbia and all opportunities, this one holds Belgrade — a division that is understood by both the visitor and the search engine.

The first signal arrived quickly: in Search Console, the query "trumpeters Belgrade" on this domain recorded an increase of views of five hundred and eighteen percent. It's an early signal, not an outcome — the full effect of such a separation is measured in weeks, and so the client was told.

Practically, the client has a website that loads immediately even on a weak connection, on which it clearly states where the price starts, which has its own page for each Belgrade municipality with settlements and typical occasions, and on which every call to action leads to the phone — the only channel through which bookings are actually agreed. The code is on the server and in the repository, with no subscription, no commission and no plugins that break on the next update.

The rest is also what is not solved by the code, and that was said openly, without embellishment. Google Business Profile does not exist, and it is the strongest lever for local inquiries in Belgrade. Eleven recordings from the performance are on someone else's channel instead of the orchestra's channel, so the video data cannot be reported to Google until the recordings are transferred. Both are up to the client and both await his decision, not a new change on the site.

FAQ

Frequently asked questions

Why maintain two websites instead of one larger site?

They serve different search intent. One covers events throughout Serbia, while the other focuses on Belgrade and its municipalities. Separating their purpose prevents duplicate pages from competing for the same searches.

Why does the site not have a contact form?

Because she didn't work. On the old site, she received zero inquiries in fourteen days, while the entire deal was negotiated over the phone. Instead of a form that creates the impression that an answer is waiting, every call to action is a phone number, with Viber and WhatsApp on the same line.

Why publish the price at all if it is negotiated by phone anyway?

Because a visitor who does not know the order of magnitude most often does not call. The site says where the price starts and what moves it, so the call serves to agree on a date, not for guessing. The final amount is said in a conversation, when the date, place and how long the game is played.

Can the municipality pages be added or changed later?

I can. The common parts of the site are in one folder, so the navigation, footer and booking block are changed in one place for every page. The new municipal page is one file per the same pattern, plus a line in the sitemap.

What does a five out of five rating that has been removed from the site mean?

It was written into the structured data on one hundred and fifty-eight pages, with no reviews displayed and no profile to come from. Google treats it as spam in structured data, and the penalty goes to the entire domain. It has been removed and replaced with verifiable data.

Why no WordPress?

Because a site of this scale has nothing to gain from it. The content changes infrequently and is changed by the artist, and in return there are no plugins to update, no database to crash, and no months where the site breaks on its own. The entire site is static pages delivered by the server without any query.

Next step

Have a project that needs to work better?

Send me your goal and current situation. I will reply with a concrete scope proposal.

Start a project